Node-RED の JSONata で月の初日と最終日を取得するメモ

この記事は Node-RED Advent Calendar 2021 の 5 日目の記事です。25 日ごろまで書かれず急に空いてしまったのでピンチヒッターです。

Node-RED の JSONata で月の初日と最終日を取得するメモです。

背景

たとえば、WordPress REST API にようなデータを取得する API で期間を与えて取得できるシーンがありますが、そのときに、その API の日付フォーマットに合わせて、ある月のデータが欲しくて初日0:00から最終日23:59まで取得したいときがあります。

初日はやりやすい

まず、初日0:00 ですね。

いろいろなやり方があるかなと思いますが、私の場合は JSONata の日付処理が使いやすいので、Node-RED の JSONata 記法でこのように書きました。

$fromMillis($millis(),'[Y0001]') & "-01-01T00:00:00"

こちらを実行すると、 $fromMillis($millis(),'[Y0001]') で、いま実行している年、つまり今年が返ってきます。あとは、固定値 "-01-01T00:00:00" がつながるので、

"2021-01-01T00:00:00"

といった値が取得できます。いままで、毎年1回だけ調査するような仕組みで、手打ちで年の部分を変えていたので、自動でできて便利。

最終日はこうしてみた

つづいて、最終日23:59 です。

月の最終日は、常に変動するので厄介です。各月をメモって固定値にすればいいかなと思いきや、うるう年の場合は2月が29日になったりして、地味にややこしいです。

なので、次の月の1日目の 0:00 から1秒だけ引いて 23:59:59 を表現することにしました。

$fromMillis(
   $toMillis(
       $fromMillis($millis(),'[Y0001]') & "-02-01T00:00:00"            
   ) - 1000,
   '[Y0001]-[M01]-[D01]T[H01]:[m01]:[s01]',
   '+0900'        
)  

ちょっと、複雑なので、インデントした複数行で載せています。JSONataってこういう風にも書けるんですよね。

具体的には、

$toMillis(
   $fromMillis($millis(),'[Y0001]') & "-02-01T00:00:00"
) - 1000,

の部分が $fromMillis($millis(),'[Y0001]') & "-02-01T00:00:00" で次の月の1日目を取得しています。

これで、2021-02-01T00:00:00 のような文字列が返ってくるので、$toMillis を通すことでミリ秒に戻して – 1000 で1秒だけ引いて 23:59:59 のミリ秒を出すようにしました。

あとは、

$fromMillis(
   その月の最終日 23:59:59 ミリ秒,
   '[Y0001]-[M01]-[D01]T[H01]:[m01]:[s01]',
   '+0900'        
)  

となり、今回の場合はタイムゾーンを日本時間で扱いたかったため第3引数に '+0900' を付与して '2021-01-31T23:59:59 と返ってきました。タイムゾーンまわりはサーバーで返すミリ秒によって色々あるかもしれないので、一度タイムゾーンなしで出力してみてどうなるかを、把握してから、行ったほうがいいと思います。

今回のフロー

image

簡単ですが、今回の知見を踏まえてこのようなフローを作りました。

[
    {
        "id": "3cac569ea15acea0",
        "type": "inject",
        "z": "4a583c59761dbc44",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 230,
        "y": 200,
        "wires": [
            [
                "0161950bd7fc8f69"
            ]
        ]
    },
    {
        "id": "0161950bd7fc8f69",
        "type": "change",
        "z": "4a583c59761dbc44",
        "name": "1月の初日と最終日を取得",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "[\t   $fromMillis($millis(),'[Y0001]') & \"-01-01T00:00:00\",\t   $fromMillis(\t       $toMillis(\t           $fromMillis($millis(),'[Y0001]') & \"-02-01T00:00:00\"            \t       ) - 1000,\t       '[Y0001]-[M01]-[D01]T[H01]:[m01]:[s01]',\t       '+0900'        \t   )     \t]",
                "tot": "jsonata"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 490,
        "y": 200,
        "wires": [
            [
                "d5d37bd89abef353"
            ]
        ]
    },
    {
        "id": "d5d37bd89abef353",
        "type": "debug",
        "z": "4a583c59761dbc44",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 750,
        "y": 200,
        "wires": []
    }
]

実行してみると、配列の0番目に初日が、1番目に最終日が返ってきます。

image

最終日 23:59:59 を出すテクニック自体は JavaScript の Date クラスで頑張ったことはあるのですが、 JSONata でも似たアプローチができて満足です!