NEWS
Datum und Zeitverarbeitung mit NodeRed
-
Da ich mich nun mal 1-2 Tage intensiv mit der "eingebauten" Datums- und Uhrzeitverarbeitung in NodeRed beschäftigt habe, würde ich meine Erkenntnisse gerne teilen und vielleicht springt ja für den einen oder anderen was nützliches heraus.
Es geht in diesem Thread nicht um die Installation von zusätzlichen Nodes, die noch weitere Steuerungsmöglichkeiten bieten, sondern das was im Grundbaukasten bereits vorhanden ist.
Als wer noch mehr zu den Themen - scheduling oder Zeit filtern sucht - der soll sich mal folgende Nodes anschauen:
- cronplus: https://flows.nodered.org/node/node-red-contrib-cron-plus - alles und noch mehr was iobroker mit dem cron als Scheduler bietet.
- lightscheduler: https://flows.nodered.org/node/node-red-contrib-light-scheduler triggert - aber vor allem filtert nach Zeitereignissen
- bigtimer - wobei ich zwar am Anfang begeistert war, aber ich inzwischen den Nutzen nicht mehr sehr hoch einschätze:
https://flows.nodered.org/node/node-red-contrib-bigtimer
Doch mir geht es ja nicht darum, mit welchen zusätzlichen Nodes man noch was machen kann, so wie man mit dem Grundbaukasten Zeit- und Datumsereignisse verarbeitet. Zum Schluss stelle ich dann noch einen kleinen Flow vor, den man als Timer nutzen kann. - und zwar flexibler als bei den fertigen Nodes.
Die Javascript Bibliothek, die in Node Red für die Datums- und Uhrzeit verarbeitung verfügbar ist, ist wohl die moment.js - und die Funktionen dieser Bibliothek werden ich nun im Verlauf weiter vorstellen - allerdings nicht über Javascript, sondern im Prinzip kann man diese ganze Bibliothek über JSONATA nutzen. Erst seit dem ich mich damit nun etwas näher beschäftigt habe, ergeben sich neuen Möglichkeiten, wobei ich auch noch hin und wieder ??? habe und deswegen vielleicht manches hier umständlich aussieht.
Dennoch kommt bei diesem Thread quasi kein natives Javascript zur Anwendung (also keine Function Nodes).
Ich denke, das im Blockly wohl auch die moment.js eingebunden ist - insofern kann man das vergleichen und gegenüberstellen.
Was im Standard nicht verfügbar ist, sind die Astrozeiten - dafür bieten sich aber alle 3 oben genannten Nodes an - die hier eine Vielzahl an Funktionen bieten - wer da mehr wissen will, meldet sich einfach.
Wie gesagt - sowohl im Blockly als auch in Node Red wurde für die Datums und Uhrzeitverarbeitung die moment.js Bibliothek verfügbar gemacht. Dabei geht in NodeRed in meinen Augen die Möglichkeiten der Nutzung über das hinaus, was üblicherweise in den Puzzleteilchen angeboten wird.
Die volle Funktionalität der moment.js Bibliothek sollte man sich als Nachschlagewerk als Seite abspeichern:
https://momentjs.com/docs/#/parsing/Anscheinend wird die Bibliothek nicht mehr weiter entwickelt - wird aber noch gewartet.
Nodes die vollkommen überflüssig sind, sind die moment.js Nodes - zumindest seitdem die moment.js Bibliothek unter NodeRed im JSONATA verfügbar gemacht wurde. Diese Nodes : https://flows.nodered.org/node/node-red-contrib-moment können also komplett ersetzt werden. Ich hatte die anfangs auch, werden nun aber rausgeschmissen.
Dreh- und Angelpunkte bei der Nutzung der moment.js Bibliothek sind natürlich die moment-Objekte. Diese sind keine date-Objekte von Javascript. Wer also die moment.js Bibliothek im Javascript nutzen will, muss sie nach Installation halt wie üblich mit require in den Code einbinden.
https://momentjs.com/docs/#/use-it/
var moment = require('moment'); // require moment().format()
In NodeRed ist es wie gesagt nicht erforderlich und man benötigt weder eine moment.js Insallation noch muss man Bibliohteken global verfügbar machen - außer man wollte die moment.js in function Nodes nutzen, was hier aber nicht der Fall ist und deswegen auch nicht weiter beschrieben wird. Hier geht es deshalb ausschließlich um die moment.js Funktionen, die JSONATA in NodeRed zur Verfügung stellt und die deshalb in verschiedenen Nodes wie der Change-Node, der Inject-Node, der Switch-Node, Join-Node etc. zur Verfügung steht und angewandt wird.
Im folgenden werde ich mich etwas an Blockly orientieren - da in meinen Augen die gleiche Bibliothek dahinter liegt und erklären, wie man das im JSONATA und in NodeRed umsetzt.
-
1. Das "Datumsobjekt" (moment)
In Blockly wird es so dargestellt:
Ich gehe wie gesagt davon aus, dass es sich intern hierbei um eine moment-Objekt und kein JS Date Objekt handelt, aber eigentlich spielt das keine Rolle, weil wir ja Funktionalitäten miteinander vergleichen.
Wenn man dieses Blockly laufen lässt bekommt man den aktuellen Timestamp - sprich die Unix Zeit in ms.
8.12.2021, 16:47:28.462 [info ]: javascript.0 (1433) script.js.common.Skript_1: 1638978448462
Das gleicher Ergebnis bekomme ich natürlich auch mit einer Inject Node:
Die normale Variante ist einfach timestamp in der Inject-Node auszuwählen. Das gleiche Ergebnis kann man auch mit JSONATA und einem moment-Objekt erzielen.
Die Ausgabe des moment-Objektes ist immer ein String, sodass wenn man es als Zahl weiterverarbeiten möchte, explizit in eine Zahl gewandelt werden muss. (das Format mit dem kleinen "x" steht für die Ausgabe in ms im UNIX time Format).
Schaut man sich nun die Möglichkeiten der Ausgabe von aktuellen Datums- und Uhrzeitteilen an, so sind hier in dem Puzzleteil 2 verschiedene Uhrzeiten zusammengemischt.
- Die grün markierten Teile zerlegen einfach das aktuelle Datum und Uhrzeit. Habe ich im Screenshot grün
markiert.
In dem Screenshot habe ich mir also zur aktuellen Uhrzeit die Sekunde ausgeben lassen. Im Log sieht man anhand des Zeitstempels, dass 3 ausgegeben wurde und der Zeitstempel des Log 03 aufweist.
Die gleiche Ausgabe der aktuellen Sekunde erhalte ich im Node-Red mit folgender JSONATA Anweisung:
Auch hier sieht man exakt die gleiche Zahl der aktuellen Sekunde stimmt mit dem Zeitstempel des Logs überein.
Die Blockly-Angaben zum extrahieren der jeweiligen Datums und Uhrzeitkomponenten finden sich alle in der entsprechenden Syntax in der moment.js Bibliothek:
Millisekunden:
$moment().millisecond()
Sekunden:
$moment().second()
Minuten:
$moment().minute()
Stunden:
$moment().hour()
Monatsdatum:
$moment().date()
Monat als Nummer:
$moment().month() + 1
Warum in der moment Bibliothek die Monate ab 0 indiziert werden, ist mir allerdings schleierhaft. Deshalb muss man hier immer +1 dazuzählen. Der Januar=0 und Dezember=11 in der internen Darstellung.
Jahr, voll
$moment().year()
Für was das "kurze" Jahr im Blockly gut sein soll - ist mir schleierhaft - also 121 aktuell - also die Anzahl Jahre nach 1900. Hier gibt es KEINE Entsprechung in der moment Bibliothek - eventuell wird in dem Blockly hier gerechnet - keine Ahnung.
für was das gut sein soll - erschließt sich mit genauso wenig, wie die Monatsindizierung in der moments- Bibliothek.
Wochentag als Nummer:
$moment().isoWeekday()
Wichtig ist hier auch den ISO Weekday zu nutzen, sonst muss man das Land (locale mit angeben), wenn man nur weekday benutzt, da in USA der 1. Tag der Woche Sonntag ist, in Europa hingegen der Montag. Also ist heute Mittwoch der 3. Tag.
Im Prinzip wird bei weekday immer mit 0 zu zählen begonnen. Insofern kommt beides Mal 3 für Mittwoch raus - aber ohne locale endet es in USA mit Samstag 6 und Sonntag 0, hier halt mit 1 und 7 für Sonntag.
Was im Blockly fehlt (nur die Wichtigsten) und die moments Bibliothek für das aktuelle Datum noch bietet sind:
Tag des Jahres:
$moment().dayOfYear()
Kalenderwoche:
$moment().isoWeek()
Quartal:
$moment().isoWeekday()
Wer die Ausgaben ausprobieren will - hier mal ein Screenshot mit dem heutigen Datum und den Flow, um auch andere Daten zu testen:
Die anderen Ausgabeformate im Blockly werden in moments durch Formatstrings abgebildet, die die Anzahl der Kombinationen im Blockly bei Weitem übersteigt, die aber durch Mehrfachaufrufe und Stingmanipulationen auch zu erreichen sind. Es wäre jetzt Unsinn das alles hier aufzulisten - zum Nachschlagen siehe hier: https://momentjs.com/docs/#/displaying/
Um also wie im Blockly den Wochentag als Text zu erhalten, passt man die Formatausgabe entsprechend an:
Falls man keine Lokalisierung beim Erstellen des Moment-Objektes angegeben hat, muss man es eben bei der Ausgabe tun. Hier erst ohne und dann mit deutscher Einstellung - gibt also ohne die englische Ausgabe, mit locale die deutsche Ausgabe.
$moment().locale('de').format('dddd')
russisch und andere Sprachen, wie italienisch kann die Bibliothek natürlich auch:
- Nun zu den beiden violett markierten Blockly-Ausgaben:
Hier wird das Datumsobjekt manipuliert und gibt zwar die aktuelle Zeit aus, aber es ist letztlich die Differenz zum Tagesanfang in einer bestimmten Einheit.
Sekunden seit Tagesanfang:
Ergebnis um 18:48:12 sind 67.692 Sekunden. (18*3600 + 48 *60 + 12) = 64.800 + 2.880 +12 = 67692
In moments geht man wie folgt vor:
- Man erstellt ein moments Objekt zur aktuellen Zeit und eines zu Beginn des Tages - lässt die Differenz berechnen und gibt sie in der gewünschten Einheit aus:
$moment().diff($moment().startOf('day'),'seconds')
Das 1. Moments Objekt ist die aktuelle Uhrzeit und es wird die Differenz zum Tagesanfang in Sekunden ausgegeben.
- Um es mit der Blockly-Ausgabe zu vergleichen, habe ich als erstes ein moments Objekt generiert zur gleichen Zeit als das Blockly lief, also um 18:48:12 und mit dem moments Objekt des Tagesanfangs verglichen.
$moment('18:48:12','HH:mm:ss').diff($moment().startOf('day'),'seconds')
Man sieht das das Ergebnis mit der Blockly Ausgabe identisch ist:
Minuten seit Tagesanfang:
lassen sich analog definieren, ich muss nur das Ausgabeformat ändern:
$moment().diff($moment().startOf('day'),'minutes')
Der Vorteil dieser Art von Berechnung mit moments mit startOf ist natürlich eine wesentliche höhere Flexibilität:
https://momentjs.com/docs/#/manipulating/start-of/
Damit ist es nun natürlich ein Leichtes sich die Anzahl der vergangenen Minuten seit Monatsanfang auszugeben oder die Anzahl der Stunden seit Monatsanfang usw. Ob es sinnvoll ist, ist eine andere Frage, aber man erspart sich doch in dem einen oder anderen Fall eine Menge Rechenarbeit.
- Die grün markierten Teile zerlegen einfach das aktuelle Datum und Uhrzeit. Habe ich im Screenshot grün
-
2. Erstellen des Datumobjektes / Konvertierung.
Um im Blockly ein Datumsobjekt zu erstellen, das nichts mit der aktuellen Uhrzeit zu tun hat, habe ich nur das Konvertierungsblockly gefunden und habe keinerlei Ahnung welche Formate erlaubt sind. Ich gehe mal davon aus, dass standardisierte Datum/Zeitsstrings funktionieren - wie genau weiß ich aber nicht und muss man wahrscheinlich ausprobieren.
Ich habe mal hier die Uhrzeit aus der letzten Berechnung der Anzahl von Sekunden seit Tagesanfang bis 18:48:12 genommen und somit geprüft, ob die Sekundenzahl 67.692 rauskommt um damit zu überprüfen, dass das Datumsobjekt korrekt erstellt wurde, was der Fall zu sein scheint.
Die Generierung eines Datumsobjektes mit der moments Biblitothek geht jedoch bei weitem über das hinaus, was hier mit bestimmten Strings zu erreichen ist und was Blockly hier bietet. Und selbst die Stringverarbeitung kann ich um Missverständnisse zu vermeiden, ganz genaue Interpretationsmöglichkeiten mitgeben. Mit Blockly muss ich den String auf eine ganz bestimmte Weise vorformatieren, dass das Datum richtig erkannt wird. Leider funktioniert es nicht einen Formatstring wie bei moments mitzugeben.
An diesem Beispiel ist das schön zu sehen:
Eingabe des Datum - Zeit Strings ist: 07.11.2021 08:55:01 - das ist die Ausgangssituation, also der 7. November um 8:55.
Wird das so im Blockly eingegeben wird es wie vermutet amerikanisch interpretiert - nämlich als 11. Juli.
Natürlich wissen das die Blockly Fans - splitten den String als Array auf und setzen ihn neu zusammen.Eine elegantere Lösung bietet hier die moments Bibliothek, die es erlaubt ein oder sogar mehrere Formatstrings mitzugeben, die dann in abnehmender Priorität ausprobiert werden, bis einer passt.
Ohne korrekten Formatstring - (orange markiert) - wird der eingegebene String auch fehlinterpretiert und als Juli interpretiert, mit korrektem Formatstring wird richtig November erkannt:
Eine weitere Erleichterung zur richtigen Interpretation des Datumsstrings ist die Angabe ob eine exakte Übereinstimmung erforderlich ist (was dann nicht der Fall ist, wenn man keine amerikanische und deutsches Datum verwendet und deshalb die Trennzeichen nicht als Erkennungszeichen dienen müssen).
Folgendes Beispiel mit 2 Datumstrings:
- 05/06/2021 21:47:27
- 07.11.2021 08:55:01
Die Ausgabe der oberen Inject Node = blau, die untere Inject-Node = orange.
Als erstes sieht man wieder bei der Ausgabe 5 - dass ohne Formatstring Monat und Tag wieder vertauscht sind.
"5: " & $moment(payload)
Ausgabe 2 ist für beide Strings richtig - da hier auf den strikten Vergleich verzichtet wurde und alles alphanummerische zwar als Trennzeichen, aber keine fester Vergleich stattfindet.
Hingegen wird mit der strikten Vergleich einmal der Datumsstring mit dem / oder . abgelehnt (orange und blau) markiert."3: " & $moment(payload, 'DD/MM/YYYY HH:mm:ss',true).format('DD.MM.YYYY HH:mm:ss')
bzw.
"4: " & $moment(payload, 'DD.MM.YYYY HH:mm:ss',true).format('DD.MM.YYYY HH:mm:ss')
Spezifiziert man true hinter dem Eingabeformatstring, dann findet der strikte Vergleich statt.
Wie vorher bereits erwähnt kann man auch mehrere Eingabeformatstrings in einem Array spezifizieren die nach absteigender Priorität verwendet werden. Auch kann man locale mitgeben - damit ist dann muss man das bei der Ausgabe nicht mehr machen.
Für die Stringeingabe habe ich deshalb diese Form - die auch timestamps akzeptiert als universell herausgefunden (Ein - und Ausgabe):
$moment(payload,['DD.MM.YYYY HH:mm:ss','x'],'de').format('DD.MM.YYYY HH:mm:ss')
Hier alles wieder zum Import und selbst ausprobieren:
Zum Schluß sei auch nochmals darauf hingewiesen, dass neben Strings auch Arrays und Objekte zum Erstellen von moments Objekten genutzt werden können.
So kann ein Moments Objekt aus{ "years": 1981, "months": 2, "days": 24, "hours": 7, "minutes": 7 }
erstellt werden. Fehlen am Rand Angaben werden diese automatisch hinzugefügt.
Also obiges Objekt gibt ein Datum von 24.03.1981 07:07:00, lässt man die Minuten weg dann wird das 24.03.1981 07:00:00, lässt man das JAhr und die Minuten weg dann 24.03.2021 07:00:00
-
3. Datums- und Uhrzeitvergleiche
Im Blockly vergleiche ich ein Datumsobjekt mit einem String oder Uhrzeit
Ob ich damit auch ein Datum vergleichen kann, weiß ich nicht und es scheint auch nur die aktuelle Zeit verglichen zu werden. Ansonsten muss man eben für den Vergleich alles in Datumsobjekte umwandeln, das ergibt ja Zeitstempel im UNIX Millisekunden Format und vergleicht diese eben miteinander - ist eigentlich auch kein großer Akt - dann hätte man sich dieses Puzzle auch sparen können.
In der Moments Bibliothek kann ich wiederum 2 Moment-Objekte oder 1 Moment-Objekt mit String - Array oder Objekt vergleichen.
Hier mal wieder alle Möglichkeiten aus der Doku: https://momentjs.com/docs/#/query/
Für die Vergleiche habe ich mal 2 moment Objekte genommen - da hatte ich dann mit JSONATA leichte Schwierigkeiten, deswegen schaut das etwas komplizierter aus.
Bei Stringvergleichen war das keine Problem, aber wenn ich 2 Moment Objekte miteinander vergleichen musste - kann ich das nicht mit einem Einzeiler - da müssen dann größere Fachleute als ich ran.
Ich habe es nun so gelöst:
( /* Vergleiche gehen nicht mit Einzeiler - wenn man nicht normale Stringvergleiche etc. nutzt */ $a := $moment(timeA,['DD.MM.YYYY HH:mm:ss','x'],'de'); $b := $moment(timeB,['DD.MM.YYYY HH:mm:ss','x'],'de'); $a.isBefore($b); )
Also beide Momentobjekte erzeugt und dann verglichen. isBefore, isAfter, isSame etc. ist ja selbsterklärend und entspricht den gleichen Möglichkeiten, die hier Blockly bietet - nur dass eben 2 Moments, Moments mit Strings oder Arrays oder Objekten verglichen werden können.
Neben den intelligenten Datums und Uhrzeitvergleichen, kann man sich diese natürlich über eine Change-Node ausgeben lassen oder direkt in einer Switch Node bearbeiten. Habe mal beide Varianten ausprobiert:
Ein Feature ist nämlich bei diesen Vergleichen noch erwähnenswert.
Beide Zeiten unterscheiden sich um EINE Sekunde.In einem Fall werden die Zeiten als identisch gesehen, im anderen Fall nicht.
Das liegt an dem optionalen Parameter bei dem man die Genauigkeit des Vergleiches festlegen kann:
( /* Vergleiche gehen nicht mit Einzeiler - wenn man nicht normale Stringvergleiche etc. nutzt */ $a := $moment(timeA,['DD.MM.YYYY HH:mm:ss','x'],'de'); $b := $moment(timeB,['DD.MM.YYYY HH:mm:ss','x'],'de'); $a.isSame($b, 'minute'); )
Im Fall der Gleichheit kann man mit dem zusätzlichen Parameter die Genauigkeit angeben.
Hier wieder die Flows zum Nachvollziehen:
-
4. Rechnen mit Datum und Uhrzeit
Hier habe ich im Blockly gar keinen Baustein gefunden. Ich gehe davon aus, dass man halt alles in Timestamps also UNIX Millisekunden umrechnet und dann rechnet.
Hier wieder die Doku:
https://momentjs.com/docs/#/manipulating/Neben dem jeweiligen Setzen des Moment-Objektes auf den Anfang oder das Ende einer Zeiteinheit Start und End of Time, gibt es die Möglichkeit Zeitspannen in die Vergangenheit oder in die Zukunft mit Text ausgeben zu lassen. Time from/to now oder Time from/to X. Also in einem Textformat sich die Zeit relativ entweder zur aktuellen ZEit oder einem beliebigen Zeitpunkt ausgeben zu lassen.
Dabei bleiben die ZEitangaben aber bewußt wage und werden nur immer exakter.
Nun zum Rechnen - da die innerhalb der Objekte funktioniert - braucht man sich um irgendwelche Wechsel oder Einheiten keine Gedanken zu machen:
Mit folgendem Einzeiler addieren ich zu aktuellen Zeit 600s dazu - also 10 Minuten (Grün im Screenshot):
$moment(payload,['DD.MM.YYYY HH:mm:ss','x'],'de').add(600,'s').format('DD.MM.YYYY HH:mm:ss')
Das tolle ist aber das ich auch ganze Objekte addieren kann, wie 600 Sekunden und 8 Stunden. (Rot im Screenshot)
$moment(payload,['DD.MM.YYYY HH:mm:ss','x'],'de').add({'seconds':600,'hours':8}).format('DD.MM.YYYY HH:mm:ss')
Leider habe ich es nicht geschafft an das duration Objekt über JSONATA ranzukommen - deshalb hab ich das wieder als JSONATA Skript mühsam zurückverwandelt. Wie gesagt - wahrscheinlich geht das eleganter - aber ich bin zu blöd dazu.
Jedenfalls um das Delta wieder herauszubekommen, habe ich folgendes JSONATA Skript gebastelt:
Erstmal wird zur aktuellen Zeit folgende Zeitstpanne hinzugerechnet:
{ "days": 8, "hours": 3, "minutes": 47, "seconds": 28 }
Die jetzige Zeit timeA plus 8 Tage, 3 Stunden, 47 Minuten und 28 Sekunden ergibt dann timeB.
Um dann wieder die Differenz zu jetzt zu berechnen - und ohne Zugriff auf das duration Objekt habe ich folgendes Skript verwendet:
( $b := $moment(timeB,['DD.MM.YYYY HH:mm:ss','x'],'de'); $a := $moment(timeA,['DD.MM.YYYY HH:mm:ss','x'],'de'); /* $b.diff($a) */ /* $moment.duration($b.diff($a)); */ /* Kein Zugriff auf duration - also manuell berechnen*/ $days := $b.diff($a, 'days'); $hours := $b.diff($a, 'hours') - 24 * $b.diff($a, 'days'); $minutes := $b.diff($a, 'minutes') - 60 * $b.diff($a, 'hours'); $seconds := $b.diff($a, 'seconds') - 60 * $b.diff($a, 'minutes'); $sec := $b.diff($a)/1000; $difference := { "days": $days, "hours":$hours, "minutes": $minutes, "seconds": $seconds }; )
Nun noch ein einfaches Beispiel wie man diese "lesbare" Format für eine Zeitdiffernz verwenden kann:
$moment().locale('de').endOf('year').fromNow()
Damit beantwortet man die Frage, wie lange dieses Jahr noch dauert?
Mit der locale gesetzt bekommt man dann diese Antwort:
Hier wieder alles zum Ausprobieren:
-
5. Abschluss
So zum Abschluss habe ich mal meine neuen Kenntnisse dazu genutzt, um mit den einfachen Boardmitteln einen Timer zu erstellen.
Einen der nicht geschwätzig ist. Wie gesagt, dass schöne an der Bibliothek ist dass man Objekte mit beliebigen Einheiten dazu addieren kann.
Hier der Flow:
Hier mal der 9 Minuten und 28 Sekunden Timer Ausgabe in lesbarer aber nicht geschwätziger Form:
Die Einheiten werden auch richtig auf und abgerundet. Wenn also in 7 Minuten ausgegeben wird, dann befindet sich er Timer zwischen 7:29 und 6:30 Minuten.und hier die geschwätzige Ausgabe mittels Angabe von verbleibenden Minuten und Sekunden.
Das Ende der Ausgabe ist noch unschön und kann man sicher anders lösen - aber das kann ja jeder für sich mal machen.
Hier noch den Timerflow zum Import:
FAZIT und ENDE:
Ich finde die moment.js Bibliothek und wie man sie in NodeRed mit Standardmitteln nutzen kann, wirklich sehr mächtig und es erleichtert einem das Hantieren mit Datums - und Uhrzeitformaten. Wie gesagt was JSONATA Programmierung oder manchmal diese Abkürzungs Syntax bedeutet, das überfordert mich leicht.Deshalb mal ganz allgemein: Wenn jemand Fragen hat gerne. Wenn mir jemand erklären kann, wie ich unter JSONATA an das Duration Objekt der moment.js Library komme - gerne.
Ich hoffe es ist OK, wenn ich hier mal das was ich herausgefunden habe, aufschreibe und hoffe auch, dass es dem einen oder anderen bei seinen eigenen Projekten hilft.
-
@mickym Saubere Arbeit !! Danke!
-
@mickym Ja, tolle Arbeit und super erklärt.
-
@mickym
1A. Mega, was für eine Herkulesarbeit. Danke dafür. -
Hab den Post weiter unten für iobroker und node-red aktualisiert und den alten gelöscht.
-
@rewenode Ja das wollte ich eigentlich verhindern - mit der externen Einbindung - da musst Du wieder an der settings.js rumbasteln, dass Du externe Bibliotheken verfügbar machst.
Konnte Deinen Flow auch nicht einbinden - da irgendein parse Error - aber Dank Deiner Erklärung konnte ich es auch nachvollziehen - allerdings auch nur in der Standalone Version:
In der function Node muss man halt die Bibliothek noch bekannt machen:
Interessant ist dass der duration strings nur bis zu Stunden auflöst - also 195 Stunden.
Habs mal mit 5 Jahre + 30 Tagen + 3 Stunden + 47 Minuten +28 Sekunden - ausprobiert. Dann kommen auch nur 44547 Stunden raus. - Damit ist der Mehrwert nun nicht so hoch - als dass sich der Streß mit Einbindung externer Bibliotheken lohnt.
-
@mickym sagte in Datum und Zeitverarbeitung mit NodeRed:
@rewenode Ja das wollte ich eigentlich verhindern - mit der externen Einbindung - da musst Du wieder an der settings.js rumbasteln, dass Du externe Bibliotheken verfügbar machst.
Konnte Deinen Flow auch nicht einbinden - da irgendein parse Error - aber Dank Deiner Erklärung konnte ich es auch nachvollziehen - allerdings auch nur in der Standalone Version:Ja, sorry. hatte es in einer stand alone version getestet.
Hier nochmal für die ioBroker Version.
Da gehts dann ganz einfach, wenn man einmal die moment.js eingebunden hat. Ein function-node ist dann nicht mehr nötig.- binde die moment.js über die settings.js ein.
Die befindet sich in
/opt/iobroker/node_modules/iobroker.node-red/settings.js
Dort muss di function functionGlobalContext um den Eintrag
moment:require("moment")
erweitert werden.
Beispiel aus meiner settings.ja
functionGlobalContext: { moment:require("moment") //'%%functionGlobalContext%%' // os:require('os'), // jfive:require("johnny-five"), // j5board:require("johnny-five").Board({repl:false}) },
Dann die node-red instanz neu starten.
Das wars.moment.duration kann in JSONata immer mit
$globalContext('moment.duration')
verwendet werden.
moment steht dann natürlich auch in function-nodes zur Verfügung.
Gruß
Reiner - binde die moment.js über die settings.js ein.
-
@rewenode Vielen Dank - die Anleitung ist sicher nützlich - wobei ich deswegen nun keine externe Bibliotheken einbinden würde. Aber in meiner Standalone Konfig habe ich das Duration Teil auch mal getestet, wie Du es wieder aufgelöst bekommst und es funktioniert !!!! - wenn man keine Wochen benutzt.
aber dann ist das Duration doch ganz nützlich - hier mal der Code:
( $b := $moment(timeB,['DD.MM.YYYY HH:mm:ss','x'],'de'); $a := $moment(timeA,['DD.MM.YYYY HH:mm:ss','x'],'de'); /* $b.diff($a) */ $d := $globalContext('md')($b.diff($a)); $difference := { "years" : $d.years(), "months":$d.months(), "weeks": $d.weeks(), "days": $d.days(), "hours":$d.hours(), "minutes":$d.minutes(), "seconds": $d.seconds() }; )
Das gute ist ja dass das richtig umgerechnet wird und wirklich Schaltjahre etc. alles berücksichtig wird.
Ausgangspunkt: 9.12.2021, 21:51:50
Als zu addierenden Zeitraum habe ich bewußt mal einen etwas redundanten Zeitraum genommen (2 Wochen und 30 Tage){ "years": 5, "months": 1, "weeks": 2, "days": 30, "hours": 3, "minutes": 47, "seconds": 28 }
Also dieser Zeitraum wird addiert und das Ergebnis ist verifiziert:
9.12.2021 + 5 Jahre = 9.12.2026
9.12.2026 + 1 Monat = 9.1.2027
9.1.2027 + 2 Wochen = 23.1.2027
23.1.2027 + 30 Tage = 22.2.2027 21:51:50
22.2.2027 21:51:50 + 3 Std. = 23.2.2027 0:51:50
23.2.2027 0:51:50 + 47 Minuten = 23.2.2027 1:38:50
23.2.2027 1:38:50 + 28 Sekunden = 23.2.2027 1:39:18Ausgangspunkt: 9.12.2021, 21:51:50
Das Duration Objekt liefert (wie gesagt - Wochen muss man rausnehmen){ "years": 5, "months": 2, "days": 13, "hours": 3, "minutes": 47, "seconds": 28 }
9.12.2021 + 5 Jahre = 9.12.2026
9.12.2026 + 2 Monate = 9.2.2027
9.2.2027 + 1 Woche = 16.2.2027
16.2.2027 + 13 Tage = 1.3.2027 ?????????
9.2.2027 + 13 Tage = 22.2.2027 21:51:50
22.2.2027 21:51:50 + 3 Std. = 23.2.2027 0:51:50
23.2.2027 0:51:50 + 47 Minuten = 23.2.2027 1:38:50
23.2.2027 1:38:50 + 28 Sekunden = 23.2.2027 1:39:18 -
@rewenode In Zukunft wäre es hilfreich Spoiler und CodeTags zu nutzen - sonst muss man immer scrollen um den Code zu kopieren.
Der Import geht aber im Moment trotzdem nicht - manche Flows mögen irgendwie nicht.
Aber egal - ich hab das ja erst mal miteinbinden können und auch testen können mit dem Durations Objekt - also vielen Dank Dir dafür.
-
@mickym sagte in Datum und Zeitverarbeitung mit NodeRed:
@rewenode In Zukunft wäre es hilfreich Spoiler und CodeTags zu nutzen - sonst muss man immer scrollen um den Code zu kopieren.
Ah, sorry. Bin halt zu selten hier. Hab's grad korrigiert und einen Reimport getestet, also bei mir gehts.
Gruß
Reiner -
@rewenode sagte in Datum und Zeitverarbeitung mit NodeRed:
@mickym sagte in Datum und Zeitverarbeitung mit NodeRed:
@rewenode In Zukunft wäre es hilfreich Spoiler und CodeTags zu nutzen - sonst muss man immer scrollen um den Code zu kopieren.
Ah, sorry. Bin halt zu selten hier. Hab's grad korrigiert und einen Reimport getestet, also bei mir gehts.
Gruß
ReinerJa mit den CodeTags geht es - da funkt der Browser nicht dazwischen. Das ist ja die Version ohne function Node aber global verfügbarer moment Bibliothek - aber wie gesagt ich habe es ja nun ausprobiert - und mit Deiner Anleitung geht es ja direkt.
( $b := $moment(timeB,['DD.MM.YYYY HH:mm:ss','x'],'de'); $a := $moment(timeA,['DD.MM.YYYY HH:mm:ss','x'],'de'); /* $b.diff($a) */ /* $moment.duration($b.diff($a)); */ $globalContext('moment.duration')($b.diff($a)).humanize() )
Aber wie gesagt - nur dafür würde ich moments nicht extra einbinden. Im Prinzip ist es halt nur die exakte Umrechnung von Zeitperioden erforderlich - ansonsten war ja gerade der Sinn des Postings - was alles mit Boardmitteln geht.
Für die eigentliche Umrechnung - für die aus meiner Sicht das Duration Objekt nützlich ist, habe ich ja unten gepostet.
-
@mickym sagte in Datum und Zeitverarbeitung mit NodeRed:
Aber wie gesagt - nur dafür würde ich moments nicht extra einbinden. Im Prinzip ist es halt nur die exakte Umrechnung von Zeitperioden erforderlich - ansonsten war ja gerade der Sinn des Postings - was alles mit Boardmitteln geht.
Ja, wollte nur eine Möglichkeit zeigen, moment.duration in node-red JSONata zugänglich zu machen.
Kann ja mal hilfreich sein, zumal man sich über diesen Trick nahezu beliebige Bibliotheken in JSONata zugänglich machen kann.Ja, und was Bordmittel angeht, sind bei mir immer die die ich zur Lösung des Problems unbedingt brauche und die in node-red prinzipiell möglich sind;-)
Aber generell stimme ich zu, auch wenn man Vieles nicht so eng sehen darf.
Ich glaube nicht, dass eine komplexe JSONata-Funktion im change-node weniger undurchsichtig ist, wie die gleiche Funktionalität per Javascript im function-node.Gruß
Reiner -
@rewenode sagte in Datum und Zeitverarbeitung mit NodeRed:
Ja, und was Bordmittel angeht, sind bei mir immer die die ich zur Lösung des Problems unbedingt brauche und die in node-red prinzipiell möglich sind;-)
Aber generell stimme ich zu, auch wenn man Vieles nicht so eng sehen darf.
Ich glaube nicht, dass eine komplexe JSONata-Funktion im change-node weniger undurchsichtig ist, wie die gleiche Funktionalität per Javascript im function-node.Grundsätzlich stimme ich Dir zu - wobei ich function Nodes wirklich nur nutze, wenn alles andere sehr umständlich ist - weil ich eben denke, dass man sich die ganze Genialität von Node Red zunichte machen kann, wenn man zuviel in function Nodes packt. Sehe ich leider sehr häufig auch in Beispielen. Letzlich kannst jeden Flow in eine function Node packen - aber dann brauche ich Node Red nicht - sondern kann ja - zumindest im iobroker - Javascript original schreiben. - OK als Standaloneversion hat man mit Node Red halt noch die Hardwareanbindung zur Verfügung. Aber die Klärung philosophischer Fragen sind dann wohl eher OT.
-
So noch ein paar Ergänzungen bzw. neue Erkenntnisse - wenn man es braucht.
Um Switch Nodes mit Zeitfenstern zu definieren - kann man die moments Bibliothek wie folgt nutzen:
Ohne 3. Parameter bei dem Moments Funktion isBetween() - kann man dann über eckige bzw. runde Klammern noch angeben, ob die Anfangs- bzw. Endzeiten aus bzw. eingeschlossen sind in dem definierten Zeitraum.
Ohne Angabe ist die Anfangszeit eingeschlossen, die Endzeit ausgeschlossen!!!Braucht man wieder über Nachtzeiträume negiert man das entweder oder nutzt den false Ausgang.
Am sichersten ist man, wenn man 2 moments Objekte vergleicht und dann auch noch die Genauigkeit spezifiziert:
$moment().isBetween($moment("10:48", "HH:mm"),$moment("17:05", "HH:mm"),'minute')
Das 1. moments Objekt ist Anfangszeit mit Format zu richtigen Interpretation, das 2. moments Objekt die Endzeit mit Format zur richtigen Interpretation und die Genauigkeit ist auf Minute gesetzt.
-
@mickym Wieder ein Test der positiv verlief mit der Moment Bibliothek.
Auch die Verarbeitung von Monatsnamen um moment-Objekte zu erstellen, funktioniert einwandfrei:
$moment(payload,'DD.MMMM YYYY','de').format('DD.MM.YY - HH:mm')
Auch Umlaute wie März werden korrekt verarbeitet.
Bei abgekürzten Monatsnamen muss man im Deutschen darauf achten, dass die Strings immer mit 4 Zeichen abgekürzt werden.
$moment(payload,'DD.MMM. YYYY','de').format('DD.MM.YY - HH:mm')
Sind die Monatsnamen 4 Buchstaben oder kürzer werden sie voll ausgeschrieben (wichtig also März ausschreiben), ansonsten die ersten 3 Buchstaben + Punkt. Eine ähnliche Absonderlichkeit ergab sich bei 3stelligen Wochentagsnamen die immer 2 Buchstaben + Punkt enthalten: