この記事は Node-RED Advent Calendar 2020 1日目の記事です。
いよいよスタートです!参加される、Node-RED UG のみなさまよろしくおねがいします!
今年もアドベントカレンダーはじまりましたね!
Node-RED の JSONata 機能について
以前も enebular で使える JSONata 式の便利 Tips といった記事を書いておりまして、Node-RED における JSONata の便利さを日々感じています。
詳しくは、英語ですが
JSONata Documentation · JSONata
こちらのドキュメントを見てみてください。
試してみるときは同サイトにある JSONata Exerciser もおすすめです。
その中でも日付と時間の扱いが便利
そう。Node-RED で、いろいろなデータを加工したり分析したりしますが、日付と時間をうまく扱いたいときって多いですよね・
たとえば、こういうとき。
- CSVやデータベースから日時を抽出したあと扱いやすいようにタイムスタンプ(1970年からのミリ秒)に直す
- タイムスタンプ(1970年からのミリ秒)からメール文面に使うイイ感じの表記にしたい
- サーバー内で時差を考慮しない UTC の時間になっているけど JST に変更にしたい
もちろん、function ノードで Date 関数を駆使してがんばれなくはないんですが、ちゃんとコードで書ききるって結構大変なんですよね。 Date.getMonth
関数が 0 はじまりで 1 足さなきゃいけなかったり、表記に合わせて 2 月を 02 として 0 で埋めたり文字列処理も絡んできます。
ということで、 JSONata で使ってて気持ちよかったユースケースを紹介します。
現在の時間を基準に30日前のISO 8601形式のタイムスタンプの文字列にする
このようなフローです。
[{"id":"b1120bcb.6c0148","type":"inject","z":"6604f6d3.1fe9c8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":200,"y":440,"wires":[["181a572a.6cc589"]]},{"id":"181a572a.6cc589","type":"change","z":"6604f6d3.1fe9c8","name":"30日前に変換","rules":[{"t":"set","p":"payload","pt":"msg","to":"$fromMillis($millis() - 86400 * 1000 * 30)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":440,"wires":[["443ee7dc.446578"]]},{"id":"443ee7dc.446578","type":"debug","z":"6604f6d3.1fe9c8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":680,"y":440,"wires":[]}]
インポートできるフローJSONです。
change ノードの中はこのようになっています。
$fromMillis($millis() - 86400 * 1000 * 30)
$millis()
現在の経過ミリ秒。それを取得したあと - 86400 * 1000 * 30
で30日前にしたのち、fromMillis
は経過ミリ秒を表す数値を、ISO 8601形式のタイムスタンプの文字列に変換してくれます。
実行してみると、このように "2020-10-31T08:12:44.044Z"
ISO 8601 形式のタイムスタンプで出力されます。
現在の時間を基準に年月日だけ取り出す
このようなフローです。
[{"id":"453f393a.0eca18","type":"inject","z":"6604f6d3.1fe9c8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":200,"y":520,"wires":[["e6d3c723.d37ec8"]]},{"id":"e6d3c723.d37ec8","type":"change","z":"6604f6d3.1fe9c8","name":"年月日だけ抽出","rules":[{"t":"set","p":"payload","pt":"msg","to":"$fromMillis($millis(),'[Y0001]/[M01]/[D01]')","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":520,"wires":[["edd80992.7ec3e8"]]},{"id":"edd80992.7ec3e8","type":"debug","z":"6604f6d3.1fe9c8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":680,"y":520,"wires":[]}]
インポートできるフローJSONです。
change ノードの中はこのようになっています。
$fromMillis($millis(),'[Y0001]/[M01]/[D01]')
$millis()
で取得した現在の経過ミリ秒を $fromMillis
の第2引数で日時のフォーマットをかけています。今回は、年4桁・月0埋めの2桁・日0埋めの2桁のフォーマットです。
実行してみると 2020/11/30 と出力されます。
フォーマットについてはこのように XPath/XQueryで使われている日時フォーマット を利用しているので、気になる方は参考にしてみてください。
時差の処理や時差の表記を加える
サーバー時間がUTCになっているけど、日本時間が欲しいといったような時差の扱いってややこしいです。こちらも、かゆいところに手が届きます。
このようなフローです。
[{"id":"2bbc92ac.a336ae","type":"inject","z":"6604f6d3.1fe9c8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"2020-11-01T00:00:00Z","payloadType":"str","x":180,"y":600,"wires":[["6e05fb54.0b8ec4"]]},{"id":"6e05fb54.0b8ec4","type":"change","z":"6604f6d3.1fe9c8","name":"時差 +9:00","rules":[{"t":"set","p":"payload","pt":"msg","to":"$fromMillis($toMillis(payload),'[Y0001]/[M01]/[D01] [H01]:[m01]:[s01] [z]','+0900')","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":600,"wires":[["7d950870.daf188"]]},{"id":"7d950870.daf188","type":"debug","z":"6604f6d3.1fe9c8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":680,"y":600,"wires":[]}]
インポートできるフローJSONです。
inject ノードから 2020-11-01T00:00:00Z
の文字列が来ると、 change ノードで、日本時間ぶんの時差+9:00を行って、時差のGMT表記も加えます。
ちなみにISO 8601形式のタイムスタンプで最後にZがつくとUTCを示します。→参考:ISO 8601 - Wikipedia
change ノードの中はこのようになっています。
$fromMillis($toMillis(payload),'[Y0001]/[M01]/[D01] [H01]:[m01]:[s01] [z]','+0900')
$fromMillis
の第3引数は +0900
という表記で時差を設定できます。また $toMillis(payload)
で inject ノードから来た 2020-11-01T00:00:00Z
という文字列を ISO 8601 形式で解釈してミリ秒に置き換えているのも便利なポイントです。
実行してみると、このように "2020/11/01 09:00:00 GMT+09:00"
と9時間の時差が処理され、GMT+09:00
も [z]
というフォーマット記述によって引き出されています。このあたり、JavaScriptでできなくもないですが、ライブラリを使わないと、結構深い処理なので JSONata で書けるのはありがたいです。
明日は
明日の Node-RED Advent Calendar 2020 の担当は kitazaki さんの「Node-RED x Sonos x MINI LED Badge」です!
楽しみです!