Google Apps Script(GAS)を使っていると、定期実行のスケジュール設定が不便に感じました。
標準の「日付ベースのタイマー」の設定画面を見ると、「午前8時〜9時」という選択肢。 この 「〜(から)」 という幅は何なんでしょうか。
8:00に来るかもしれないし、8:59に来るかもしれない。Googleのサーバーの機嫌次第で実行時間が最大1時間ズレる仕様です。
「毎朝8時30分までにデータを揃えたい」といった要件があるとき、このアバウトさは致命的です。
標準機能でできないなら、コードで殴るしかありません。私には他の方法が思いつかないです。
ということで、毎日決まった時刻にトリガー実行させる方法を共有します⭐️
仕組み
シンプルに「毎日深夜に『今日の08:30に実行するタイマー』をセットする関数」を作って動かす仕組みです。
1、深夜(セットアップ) GAS標準のトリガーで setDailyTrigger を動かします。この時点では「準備をするだけ」なので、実行開始時間が多少ズレても問題ありません。
2、予約 setDailyTrigger の中で「今日の08:30」のDateオブジェクトを生成し、その時刻ピンポイントのトリガー(TimeBased)をプログラムコードから作成します。
3、本番実行 予約しておいたトリガーが作動し、本命の処理 mainFunction が定刻通り(分単位)で実行されます。
コード
下記三つのコードをコピペして、時間とか関数とか適当に書き換えてください。
// ↓↓↓ ここに設定したい時間を記入 ↓↓↓
const TARGET_HOUR = 8; // 時 (24時間表記)
const TARGET_MINUTE = 30; // 分
/**
* 1. 毎朝実行する関数(セットアップ用)
* これを「毎日 AM 0時〜1時」などの早朝に実行されるよう標準機能のトリガーで設定する
*/
function setDailyTrigger() {
const functionName = "mainFunction"; // 実行したい本命の関数名
// 1. 既存の同名トリガーがあれば削除(重複防止)
// 万が一前日のトリガーが残っていたり、手動で連打した時のための防止策
const triggers = ScriptApp.getProjectTriggers();
for (const trigger of triggers) {
if (trigger.getHandlerFunction() === functionName) {
ScriptApp.deleteTrigger(trigger);
}
}
// 2. 今日の日付で、指定した時間のDateオブジェクトを作成
const targetTime = new Date();// ここで今日の日付取得
targetTime.setHours(TARGET_HOUR);// そこから時間を変更
targetTime.setMinutes(TARGET_MINUTE);// そこから分を変更
targetTime.setSeconds(0);
// 3. ピンポイントの時間指定で新しいトリガーを作成
ScriptApp.newTrigger(functionName)
.timeBased()
.at(targetTime) // atHourではなくatを使うのがポイント
.create();
console.log(`${targetTime.toLocaleString()} にトリガーをセットしました`);
}
/**
* 2. 本命の処理
* 指定した時間に実行される関数
*/
function mainFunction() {
// === ここにやりたい処理を書く ===
console.log("おはよう!");
}
トリガー設定
コードを書いただけでは動きません。起動させるきっかけとなる「トリガー設定」が必要です。GAS標準機能のトリガー設定を行います!
この記事に辿り着く人ならすでに知っている方が多いと思いますが念のため書きます。
左側のメニューから時計アイコン(トリガー)をクリック。
右下の「トリガーを追加」ボタンをクリック。
以下の設定で保存します。
実行する関数: setDailyTrigger (mainFunctionではないので注意)
イベントのソース: 時間主導型
トリガーのタイプ: 日付ベースのタイマー
時刻: 午前 0時 〜 1時 (指定したい時間より前であれば何時でもいいですが、日付を跨ぐと死にます。)
これで、深夜に適当な動き出したGASが、正確な時刻でメイン関数のトリガーセットしてくれます。
ここで一つの疑問が浮かぶと思います。
「毎日トリガーを生成したら、トリガーが無限に増えて制限(1プロジェクト20個)に引っかかるのでは?」 「実行後に削除処理を書かなくていいのか?」
結論から言うと、メインの関数(mainFunction)の中に削除コードを書く必要はありません。
理由は2つあります。
-
Googleが勝手に消してくれる
今回使った .at(time) で作成されたトリガーは、ワンタイム(1回限り)トリガーという扱いです。 指定した時間に実行が終わると、Googleのシステムが自動的にトリガー一覧から削除してくれます。 -
セットアップ関数で掃除している
念には念を入れています。setDailyTrigger の冒頭に以下の処理を入れています。
const triggers = ScriptApp.getProjectTriggers();
for (const trigger of triggers) {
if (trigger.getHandlerFunction() === functionName) {
ScriptApp.deleteTrigger(trigger);
}
}
Googleの自動削除が不発だったり、手動でセットアップ関数を叩いて重複トリガーができた時のための安全策です。
毎朝トリガーをセットする直前に「古い同名のトリガーがあったら全部消す」という動きをするので、ゴミが溜まることはありません。
まとめ
本来ならGUIの設定画面で「08:30」と入力できれば済む話なのですが、それをさせてくれないのでこのような回りくどい実装が必要です。
とりあえずこれで、Googleのご機嫌に左右されず、定刻通りにスクリプトを動かすことができます。
ほな、みなさん良きGASライフを!☀️