PowerAutomateで「土日・祝日」以外の日付を利用した処理を行う

今回はPowerAutomateでフローを作成するときのヒント。

# Problem

PowerAutomateのワークフローで「土日・祝日」以外の日付を割り出して処理に用いたい。

例えば、アンケートの回答依頼およびリマインドを行うフローのなかで、フローの起動した日時が回答期限日のN日前から当日まで（あるいは、決まった日付の当日からN日後まで）に該当する場合のみ、メインのロジック（回答依頼もしくはリマインド）を行いたいようなケース。
土日や祝日は非営業日なので依頼やリマインドをしても意味がない。「N日前」（あるいは「N日後」）を勘定する中で「土日・祝日」は除外したい。

`weekday()`関数を使用すれば曜日はわかるから土日は簡単に除外できる。しかし祝日はそうはいかない。

# Solution

Outlookにデフォルトで存在する「日本 の休日」予定表を利用する。

フローのサンプルを示す。まずは全体像：

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/b591011a-d039-7548-37a5-b239e7941336.png)

1. __イベントの取得（V4） japaneseHolidays__ ─「日本 の休日」予定表のイベント（予定）を取得。過去〜現在〜未来の90件のイベントが得られる。
1. __選択 holidays__ ─イベントの開始日時`start`のみ、それもISO8601表記の`T`より前部分（つまり日付部分）のみを抽出した配列を作成。
1. __選択 732days__ ─フロー実行日時の前365日・後365日（「後」には当日を含む）の日付の配列を作成。このあとの処理でここから土日・祝日を除外する。
1. __アレイのフィルター処理 nonHoliWeekDays__ ─フロー実行日時の前365日・後365日の配列から土日と祝日に該当するものを削除。後続アクションでこの配列を使って、「土日・祝日」以外の日付を利用した処理を実行する。

次に個々のアクションのパラメーター：

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/d2ce8f5d-4922-9605-627d-9261db7ce74f.png)

予定表IDで「日本 の休日」を選択。フィルタークエリを指定して最小限の結果セットを得ようとしたが、`start le ...`などの比較演算子を使用するとエラーになってしまった。このため何も指定していない。
このアクションを実行すると次のようなイベント（予定）の一覧を示すJSONが得られる。ここには過去〜現在〜未来の90件のイベントが含まれる：

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/3603c5dd-28eb-37a8-91e8-208a6bce95f4.png)

これを次のアクションで加工：

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/144fbfd7-f0b9-6a0b-e9c2-75a4ff4ddd86.png)

`start`プロパティのみ、それもISO8601表記の`T`より前側（日付部分）のみを要素とする配列に変換する。

From: `@{outputs('イベントの取得_(V4)_japaneseHolidays')?['body/value']}`
Map: `@{first(split(item()?['start'],'T'))}`

結果得られる配列のイメージは次のような感じ：

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/757c13a3-86ff-bc48-859c-2e06f522c0aa.png)

もう1つ日付の一覧を作成：

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/a7df8236-3379-6ccf-05f9-6e75e8de08d1.png)

こちらはフロー実行日時の前後の2年分の日付からなる配列を作成。

From: `@{range(-365,732)}`
Map: `@{formatDateTime(addDays(utcNow(),item()),'yyyy-MM-dd')}`

結果得られる配列のイメージは次のような感じ：

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/74e33b71-9b91-e102-c220-f95961ad1c08.png)

以上の2系統の配列を利用して「土日・祝日」以外の日付からなる配列を作成：

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/1c6aaee7-ff70-dd23-3146-f2f6d6d01ed4.png)

フィルタークエリで先ほどの2年分の日付のリストから土日と祝日を除外。

From: `@{body('選択_732days')}`
Filter Query: `@equals(@{or(contains(body('選択_holidays'),item()),equals(mod(dayOfWeek(item()),6),0))},false)`

結果得られる配列のイメージは次のような感じ：

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/1fd53abb-acba-c47a-c1fe-d50d2ee2a001.png)

## 使用例1　フロー実行日の「3日後」（ただし土日・祝日を除く）を割り出す

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/00b1c65c-a81d-c9ea-ead1-da6dfd6d986c.png)

__アレイのフィルター処理 greaterThanToday__ の Filter Query: `@greater(@{item()},@{formatDateTime(utcNow(), 'yyyy-MM-dd')})`

__作成 3daysLater__ の 入力： `@{body('アレイのフィルター処理_greaterThanToday')?[2]}`

## 使用例2　特定日付の「3日前」（ただし土日・祝日を除く）を割り出す

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/28b6071f-44bb-b90c-7e05-0df584f67d97.png)

__作成 targerDate yyyy-MM-dd__ の 入力： `@{formatDateTime(addDays(utcNow(),0),'yyyy-MM-dd')}`
__作成 targetDatePosition__ の 入力： `@{div(indexOf(join(body('アレイのフィルター処理_nonHoliWeekDays'),''),outputs('作成_targetDate_yyyy-MM-dd')),10)}`
__作成 targetDateMinus3__ の 入力： `@{body('アレイのフィルター処理_nonHoliWeekDays')?[sub(outputs('作成_targetDatePosition'),4)]}`

:::note
今回は「土日・祝日」以外の日付を求める方法を示しましたが、この応用で「営業日」（非営業日以外）を求めることもできるでしょう。非営業日を列挙したOutlookの予定表を作成し、定期的にメンテナンスをしておく。そしてその予定表を使ってワークフローで「営業日」を求めるわけです。デフォルトで備わっている「日本の 休日」予定表と違ってメンテナンスの手間がありますが・・・
:::