※本記事はプロモーションを含みます
目次
スプレッドシートの集計業務や日次の売上レポート作成、Slackやメールへの定時通知など、定常業務を自動化する上で「GAS トリガー 毎日 実行」の設定は避けて通れません。手作業でスクリプトエディタを開いて実行ボタンを押す運用から脱却し、夜間や始業前の決まった時間にスクリプトを走らせる仕組みを組むことで、業務の自動化は一気に実用レベルへ引き上がります。ただしGASのトリガーにはプログラムから動的に生成できる柔軟さがある反面、メソッドの命名から受ける直感的な期待とGoogleのインフラ側での実際の挙動にギャップがあり、運用段階で頭を抱えるケースが少なくありません。なので毎日確実に処理を完結させるためのコード構築と、実務で必ず直面する落とし穴への対策を整理しましょう。
atHourメソッドが「9時ジャスト」を保証しない仕様と誤解
毎日決まった時刻に関数を動かしたいとき、GASで時間主導型トリガーを組むコードを書くなら、多くの場合 atHour() というメソッドを使用することになります。たとえば「毎朝9時に処理を実行したい」と考えたとき、atHour(9) と書けば午前9時00分00秒にスクリプトが発火すると期待してしまいがちです。ただしこれが最初の大きな落とし穴になります。
Google Apps Scriptの公式仕様において、時間主導型(time-based)のトリガーで指定する atHour(n) は、「n時0分」という特定の1点を指定する命令ではありません。というのは実際の挙動が「n時からn+1時までの間のどこか(1時間のランダムな枠内)」に実行されるという時間枠の指定だからです。なので atHour(9) と設定した場合、スクリプトが走り始めるのは9時00分かもしれないし、9時23分かもしれないし、9時58分になる可能性もあります。Googleの巨大な共有クラスタ上で膨大なスクリプトが同一の「00分」に集中して負荷が跳ね上がるのを防ぐため、システム側で意図的に分散実行(ジッター)が行われているためです。
この仕様を把握していないと、社内運用でトラブルに発展します。「毎朝9時の始業ミーティングで使うスプレッドシートの集計を自動化したはずなのに、9時5分に見たら前日のデータのまま更新されていない」「9時40分になってようやく通知が届いた」という事態が起き、プログラムの不具合を疑って調査に時間を浪費することになります。ただしコード自体にエラーが出ているわけではなく、Googleのクラウドインフラが仕様通りに処理を遅延・分散させているだけです。
もし業務上、「毎朝9時00分から9時05分の間に必ず集計が終わっていなければ困る」という厳格なデッドラインが存在する場合、atHour(9) ではなく atHour(8)、あるいはさらに早い atHour(7) に設定しておく必要があります。というのは8時台のどこかで実行が始まれば、どれほど分散されて遅れたとしても9時00分までには余裕を持って完了させることができるからです。なのでトリガーの時間指定は「実行したい時刻そのもの」ではなく「完了していなければならない時刻から逆算した許容枠」で捉えましょう。
毎日決まった時間帯に実行するトリガー生成コード
GASの時間主導型トリガーは、管理画面から手動で設定することもできますが、スプレッドシートの初期設定スクリプトや保守用の関数としてコード側から生成できるようにしておくと、プロジェクトの複製や別シートへの展開が極めてスムーズになります。というのはスクリプト側でトリガーを管理する基本となるのが ScriptApp.newTrigger() を起点としたチェーン記法だからです。
以下のコードは、指定した関数を毎日午前9時台に1回自動実行するトリガーを生成する基本的な記述です。なので集計用関数 myFunction をターゲットとして、日次実行のスケジュールを登録しましょう。
function setDailyTrigger() { ... }
このコードを実行すると、GASのバックエンドに時間主導型トリガーが1件登録されます。timeBased() で時間主導型であることを宣言し、everyDays(1) で1日ごとのインターバルを指定、atHour(9) で実行時間枠を午前9時台に設定した上で、最後の create() によって実体が生成されます。なので登録されたトリガーは、コード上だけでなくプロジェクトの管理画面にあるトリガー一覧にも即座に反映され、視覚的にも確認可能です。
このコードをいつ使うかというと、基本的には「初回セットアップ時」や「年度更新などでシートを新しく作り直した際」です。ただしこの簡潔な記述をそのまま運用に乗せるには重大なリスクが存在します。というのはこの関数を呼び出すたびに「新しいトリガーが無制限に追加されてしまう」という挙動があるからです。テスト目的でこの関数を2回実行すれば、午前9時台に myFunction を呼び出すトリガーが2個登録されます。なので翌朝9時台になると同じ集計処理やメール通知が2回連続で走ることになり、データの多重集計や取引先への重複通知といった致命的な事故を引き起こします。
実行のたびに増殖する重複トリガーの防止策
トリガーをコードから自動生成する際に最も警戒すべきトラブルが、この「重複登録による多重実行」です。手動で管理画面から設定するなら目視で気付けます。ただしセットアップ関数やメニューバーからの実行、あるいは何らかのバッチ処理の中で ScriptApp.newTrigger() を呼ぶ設計にしていると、意図せず何十個もの同一トリガーがバックグラウンドに積み上がっていきます。
GASのプロジェクトには1ユーザーあたりのトリガー総数に上限(1プロジェクトあたり20個まで)が設けられているので、重複作成を放置しているとある日突然上限エラーで一切のトリガーが動かなくなります。なのでこの事態を回避するためには、新しいトリガーを作成する前に、すでに同名の関数をターゲットにしたトリガーが存在していないかを走査し、見つかった場合は一度削除してから作り直す、あるいは作成をスキップするという判定処理を必ず挟みましょう。
以下に、重複を完全に排除した上で、安全に日次トリガーを再設定するための実務的なコード例を示します。
function setSafeDailyTrigger() { ... }
このロジックを導入することで、setSafeDailyTrigger() を誤って何度連続で実行したとしても、常にプロジェクト内に存在する myFunction 向けのトリガーは「最新の1件だけ」に保たれます。というのは既存トリガーを ScriptApp.getProjectTriggers() で配列として取得し、ループ内で getHandlerFunction() を確認して対象関数と一致するものを ScriptApp.deleteTrigger() で物理削除してから、最後に create() を呼ぶという手順を踏んでいるからです。
社内で複数人が触る可能性のあるスプレッドシートや、設定用のカスタムメニューからトリガーを登録させる仕様にする場合は、単に create() するだけのコードを置くのは危険です。なので必ずこの「事前走査と削除」をセットにしたガードロジックを組み込んでおくことが、不要な障害を防ぐ鉄則となります。
タイムゾーンのズレと実行エラーを早期検知する運用
時間主導型トリガーを正しく設定したにもかかわらず、「なぜか深夜に変なタイミングで動いてしまう」「朝9時に設定したのに夕方に動く」という問い合わせを受けることがあります。というのはこの現象の主原因が、スプレッドシート側とGASプロジェクト側の「タイムゾーンの不一致」だからです。
GASには、スクリプトプロジェクト自体の設定ファイルであるマニフェスト(appsscript.json)に記述されたタイムゾーンと、紐付いているスプレッドシートのプロパティで設定されたタイムゾーンの2種類が存在します。新規でスクリプトを作成した際、環境によってはプロジェクトのタイムゾーンがデフォルトで America/New_York や Etc/GMT になっているケースがあります。なのでもしスクリプトのタイムゾーンがアメリカ東部時間のまま atHour(9) を設定してしまうと、日本時間の22時や23時にトリガーが発火してしまいます。
コード上で atHour() を扱う際は、プロジェクト設定のマニフェストファイルを確認し、"timeZone": "Asia/Tokyo" に正しく設定されているかを確認しましょう。ただしスプレッドシートの表示形式が日本時間になっていても、スクリプト本体のタイムゾーンが異なっていれば、トリガーはスクリプト側のタイムゾーンに従って動作します。
また、毎日決まった時間に無人で自動実行されるスクリプトだからこそ、「失敗に気付けない」というリスクを最初からケアしておく必要があります。GASのトリガー実行が例外エラーで落ちた場合、デフォルトではスクリプトの作成者にエラー通知メールが届きます。ただし配信頻度設定によっては1日1回のサマリー通知になってしまい、朝の障害に夕方まで気付けないといった致命的なタイムラグを生みます。
日次の自動実行関数の中身には、全体を try...catch 構文で囲み、エラーが発生した瞬間にチャットツール(SlackやGoogle Chatなど)のWebhookへエラーメッセージとスタックトレースを即時投稿する仕組みを組み込んでおくのが堅牢です。というのはトリガーによって毎日自律的に動く仕組みを整えるということは、人の目から離れて運用されることを意味するからです。なので実行時間のブレ、重複登録の排除、タイムゾーンの整合性、そして障害検知の導線までをひとまとめにして設計しましょう。無料枠や利用容量の表示は公式サイトでの表記が優先されます。

