こんにちは。SCSK渡辺(大)です。
シティーハンターの実写映画が続編制作決定したらしく、来年の配信が楽しみです。
今回はPower Automate(以下、PA)に業務で触る機会があったので、その時にAIに色々と相談して知った小技について書いてみました。
長くなったフローを「スコープ」でスッキリまとめる
概要
複雑なフローを作ると、アクションの数が数十個になり、スクロールするだけで一苦労……という状態になりがちです。 そんな時に大活躍するのが「スコープ(Scope)」アクションです。
スコープを使えば、関連する複数のアクションを1つの箱(グループ)にまとめることができます。処理の塊ごとにスコープで囲んで名前を付けておけば、フロー全体の見通しが劇的に良くなり、後からのメンテナンス性も格段に向上します。
展開するとまとめられたアクションを確認することができます。
設定方法
「スコープ」というアクションを追加し、名前を変更します。
ドラッグ&ドロップでまとめたいアクションを移動します。
土日・祝日を除外して「営業日」をカウントする
概要
「申請日から3営業日後を期限にしたい」といった要件は実務フローにおいて頻出しますが、標準の addDays 関数では土日や祝日もそのままカウントされてしまいます。
これをPAで正確に計算するための小技が「Outlookのイベント取得」と「ループ処理」の組み合わせです。
設定方法
計算中の日付を今日で初期化
varTargetDateは後述の「今の日付に+1日するための前処理」の中で使用する変数です。
本題とズレるため式の説明は書略しますが、このアクションより前で取得したMicrosoft Formsの回答日付を”今日”として変数を初期化している、というだけです。
日付カウントを0で初期化
varCountは後述の「ループ処理(1日進めて平日か判定する処理)」の中で使用する変数です。
祝日含めたカレンダーを取得
まずはOffice 365 Outlookコネクタの「イベントの取得」アクションを使い、祝日や会社の休日が登録された「予定表」から休日予定を配列として取得します。
ここで言う「予定表」とはPAと接続しているユーザーのOutlook予定表の事です。
この時、詳細パラメーターの「フィルタークエリ」に以下を入力します。
この式は、一言でいうと「予定の開始日が、今日以降のものだけを取得してください」という命令文です。
Start/DateTime ge 'formatDateTime(utcNow(), 'yyyy-MM-dd')'
式を分解してみましょう。
Start/DateTime
⇒これは、Outlookカレンダーの「予定の開始日時」という項目(列)を指しています。
ge
⇒これは算数でいうところの 「≧(以上)」 を意味する記号です。
英語の「greater than or equal to」の頭文字を取っています。
‘formatDateTime(utcNow(), ‘yyyy-MM-dd’)’
⇒ここはPAの関数(数式)です。
utcNow()
⇒今の現在時刻を取得する
formatDateTime( ~ , ‘yyyy-MM-dd’)
⇒その時刻を「年-月-日」という文字の形に整えます。つまり、「今日の日付」を作り出しています。
全て繋げると、「予定の開始日時(Start/DateTime)」が、「今日の日付」と「同じか、それより未来(ge)」のデータを取得するための式になります。
アウトプットのイメージは以下です。
[
{
"Subject": "部内定例会議",
"Start": "2024-05-01T10:00:00.0000000",
"Location": "第1会議室",
"IsAllDay": false
},
{
"Subject": "憲法記念日",
"Start": "2024-05-03T00:00:00.0000000",
"Location": "日本",
"IsAllDay": true
},
{
"Subject": "みどりの日",
"Start": "2024-05-04T00:00:00.0000000",
"Location": "日本",
"IsAllDay": true
}
]
日本の祝日を取得
先ほどOutlookから取得した予定表の中には、自分で入れた別の予定や、他の国の祝日などが混ざっている可能性があります。
そこで、「アレイのフィルター処理」アクションを使って、純粋な「日本の祝日」だけを抽出(絞り込み)します。
Outlookの標準の祝日カレンダーは、予定の「場所(location)」という項目に「日本」という文字がセットされています。
このルールを利用して、「場所が『日本』となっている予定だけを残してください」というフィルターをかけています。
このひと手間を挟むことで、自分独自の予定や海外の祝日を誤って営業日カウントから除外してしまう事故を防ぎ、正確な「日本の営業日カレンダー」を作ることができます。
アウトプットのイメージは以下です。
[
{
"Subject": "憲法記念日",
"Start": "2024-05-03T00:00:00.0000000",
"Location": "日本",
"IsAllDay": true
},
{
"Subject": "みどりの日",
"Start": "2024-05-04T00:00:00.0000000",
"Location": "日本",
"IsAllDay": true
}
]
祝日リストの作成
前のステップで抽出した「日本の祝日」データには、件名や場所、時間など、さまざまな情報が混在しています。
このままだと、後の処理で「明日が祝日かどうか?」を判定(比較)するのが非常に難しくなります。
そこで、「選択」アクションを使って、複雑なデータから「日付の文字列(yyyy-MM-dd)」だけを抜き出した、シンプルなリストに変換します。
formatDateTime(item()?['Start'], 'yyyy-MM-dd')
式を分解してみましょう。
item()?[‘Start’]
⇒ループ処理で取り出している一つ一つの祝日データの中から、「開始日時(Start)」だけを抜き取ります。
formatDateTime( ~ , ‘yyyy-MM-dd’)
⇒抜き取った日時から「時間(00:00など)」を切り捨てて、「年-月-日」だけのスッキリした文字に整えます。
アウトプットのイメージは以下です。
[ "2024-05-03", "2024-05-04" ]
ループ処理(1日進めて平日か判定する処理)
「Do until」は、指定した条件を満たすまで、中のアクション(1日進めて平日か判定する処理)をぐるぐると繰り返し実行するアクションです。
この「ループ停止条件」を正しく設定しないと、無限ループに陥ってしまいます。
int(variables(‘varCount’))
⇒カウントを貯めている変数 variables(‘varCount’) は、平日と判定された時だけ「+1」されていく変数です。
int(30)
⇒目標とする営業日数 今回は例として「30(営業日)」を設定しています。(3営業日なら int(3) とします)
つまり、真ん中を「以上」にしているので、「平日カウントが目標の日数(30)に到達するか、それを超えたら、このループを終了してください」という命令になります。
件数は「最大○○回ループしたら、条件を満たしていなくても強制的にループを終了する」という設定です。
30営業日先を探すために、念のためカレンダーを最大60日分(約2ヶ月分)まで調べる、という安全なリミッター(無限ループ防止策)になっています。
タイムアウトのPT1H 「処理が1時間(1 Hour)経っても終わらなければ強制終了する」という設定です。
今の日付に+1日するための前処理
ループの中に入ったら、まずは「今チェックしている日付」を1日未来へ進める必要があります。
そこで、「作成」アクション(アクション名:今の日付に+1日するための前処理)を使って、日付を+1する計算を行います。
addDays(variables('varTargetDate'), 1)
式を分解してみましょう。
variables(‘varTargetDate’)
⇒「現在計算の対象となっている日付」が入っている変数です。(ループが始まる前は「今日」の日付が入っています)
addDays( 対象の日付 , 追加する日数 )
⇒PAで日付を足し算・引き算するための専用関数です。今回は 1 を指定しているので、「対象の日付に1日足してください」という計算になります。
つまり、この式を実行することで「対象の日付の『明日』の日付」が作り出されます。
アウトプットのイメージは以下です。
ループ1回目
⇒計算前の変数(varTargetDate): “2026-03-01”
このアクションの出力結果: “2026-03-02T00:00:00.0000000Z” (←3月2日に進む)
ループ2回目
⇒計算前の変数(varTargetDate): “2026-03-02”
このアクションの出力結果: “2026-03-03T00:00:00.0000000Z” (←3月3日に進む)
ループ3回目
⇒計算前の変数(varTargetDate): “2026-03-03”
このアクションの出力結果: “2026-03-04T00:00:00.0000000Z” (←3月4日に進む)
今の日付に+1日する
前のステップ(前処理の作成アクション)で「現在の日付に+1日した日付(明日の日付)」を一時的に計算しました。
しかし、計算しただけでは変数のデータは古いままなので、ここで「変数の設定」アクションを使って、新しい日付に上書き(アップデート)します。
アウトプットのイメージは以下です。
【ループ開始前】 (初期値): “2026-03-01”
【ループ1回目】通過後: “2026-03-02T00:00:00.0000000Z” (←3月2日に上書きされた)
【ループ2回目】通過後: “2026-03-03T00:00:00.0000000Z” (←3月3日に上書きされた)
【ループ3回目】通過後: “2026-03-04T00:00:00.0000000Z” (←3月4日に上書きされた)
「平日ならカウントを進める(True)」、「休みなら何もしない(False)」
カレンダーを1日進めたら、次はその日が「カウントすべき平日」なのか、「スキップすべき休み(土日・祝日)」なのかを判定しなければなりません。
ここで使うのが「条件(Condition)」アクションです。
画像の設定を見ると、左側の入力欄に長い数式が入り、それが true(真)と等しいかどうかを判定しています。
この長い数式は、一言でいうと「この日は『休み』ではないですよね?」とシステムに問いかけるための数式です。
not(or(equals(dayOfWeek(variables('varTargetDate')), 0), equals(dayOfWeek(variables('varTargetDate')), 6), contains(body('祝日リストの作成'), formatDateTime(variables('varTargetDate'), 'yyyy-MM-dd'))))
式を分解してみましょう。
dayOfWeek(variables(‘varTargetDate’))
⇒先ほど1日進めた対象の日付(varTargetDate)から、曜日を「数字」で出してくれる関数です(0=日曜、1=月曜… 6=土曜)。
equals(…, 0) / equals(…, 6)
⇒先ほどの曜日の数字が「0(日曜日)」と等しいか?、「6(土曜日)」と等しいか?という判定です。
contains(body(‘祝日リストの作成’), formatDateTime(variables(‘varTargetDate’), ‘yyyy-MM-dd’))
⇒これは、「祝日リストの作成」で作っておいたシンプルな祝日リスト(body(‘祝日リストの作成’))の中に、今チェックしている日付(varTargetDate を yyyy-MM-dd に整形したもの)が「含まれていますか(=祝日ですか)?」という判定です。
or(式1, 式2, 式3)
⇒ or 関数は、カッコの中の条件のうち「どれか1つでも当てはまればTrue」を返します。つまり、上の3つの条件をまとめて「日曜日・土曜日・祝日のいずれか(=休みである)」という判定のカタマリを作っています。
not(…)
⇒ not 関数は、結果を「逆」にする関数です。先ほどの or で作った「休みである」という判定結果を逆転させ、最終的に「休みではない(=平日である)」という判定にひっくり返しています。
判定のイメージは以下です。
対象日が「2026年3月3日(火)」の場合
⇒日曜・土曜か?: 火曜日(2)なので「違う」
⇒祝日か?: リストに無いので「違う」
⇒数式の最終結果: 「休み」ではない = true(真)
⇒アウトプット(進む道): 同様に条件を満たすため、緑色の「True(はい)」ルートに進みます。
⇒営業日カウント(varCount)がさらに「+1」され、順調に日数が加算されていきます。
対象日が「2026年3月20日(金・春分の日)」の場合
⇒日曜・土曜か?: 金曜日(5)なので「違う」
⇒祝日か?: リストに含まれているので「合っている(祝日だ)」
⇒数式の最終結果: 祝日なので「休み」である = false(偽)
⇒アウトプット(進む道): 条件が true と等しくないため、赤色の「False(いいえ)」ルートに弾かれます。
⇒この中にはアクションが0件なので、カウントは増やさずにスルーして次の日へループが回ります。
まとめ
GUIでフローを作成できるため初心者にも優しいですね。
その一方で、複雑な処理を実装するのは時間が掛かると感じました。
AIの更なる発展によってPAが今後どのように変わっていくのか気になります。

















