LoginSignup
4
1

GASの定期実行をピッタリの時間に行う方法

Last updated at Posted at 2023-12-22

はじめに

この記事は「Ateam LifeDesign Advent Calendar 2023」で完走賞を狙って25記事書いているうちの23日目の記事です。ようやく終わりが見えてきた・・・!

作るもの

GASはトリガーを使うことでスクリプトを定期実行することができます。
ただ日次や週次、月次で定期実行するトリガーは以下の写真のように実行時間を1時間単位でしか指定することができません。
スクリーンショット 2023-12-17 21.17.00.png

しかし実際の運用シーンを考えると「このスクリプトは毎日必ず3時ちょうどに実行したい」ってなることもありますよね。なので今日は通常のトリガー登録からは実現できない時間をピンポイントで指定した定期実行の登録の仕方を見ていこうと思います。

実装方針

上記の仕様を実現するためには以下のポイントを使います。

ポイント1:日付を指定した場合は時間ちょうどの実行指定が可能

スクリーンショット 2023-12-17 21.23.00.png

GASは定期実行の場合は先程のように1時間ごとでの指定しかできませんが、時間ベースのトリガーとよばれる「特定の日時」に実行するトリガーの場合は時間まで指定することができ、指定された時間ちょうどにスクリプトが実行されます。

ポイント2:トリガーはスクリプトから動的に登録することも可能

先程の画面からトリガーを登録することが多いと思いますが、実はトリガーはスクリプト上からもScriptApp.newTriggerというメソッドを呼び出すことで登録ができます。

つまり今回はスクリプトが実行されると、そのスクリプト内で、翌日に実行する分の時間ベースのトリガーを登録する処理をいれることにより、毎日決まった時間ちょうどにスクリプトが定期実行されるようにしようと思います。

GASを書いていく

  var triggerDay = new Date();
  triggerDay.setDate(triggerDay.getDate() + 1);
  triggerDay.setHours(3);
  triggerDay.setMinutes(0);
  triggerDay.setSeconds(0);

まずスクリプトが実行されたタイミングの翌日3時に次のスクリプトの実行トリガーを登録するために翌日3時の日時を取得します。triggerDaysetDateで今から24時間後の日時をセットしたうえで、日にちはそのままでよくて時間はをsetHourssetMinutessetSecondsを使って3時ちょうどに指定します。

  ScriptApp.newTrigger('myFunction')
    .timeBased()
    .at(triggerDay)
    .create();

その後トリガーの登録を行います。トリガーの登録にはScriptApp.newTrigger関数を使います。トリガー名を指定し、今回は日時を指定するのでtimeBased()を呼び出し、実行する日時triggerDayを指定し、最後にcreate()することでトリガーが登録されます。

  var scriptProperties = PropertiesService.getScriptProperties();
  scriptProperties.setProperty('TriggerSetAt', triggerDay.toString());

PropertiesServiceは、スクリプト全体でデータを保存するための機能を提供しています。setProperty関数を呼び出すことで、指定された名前のプロパティに値を設定します。つまりTriggerSetAtという名前のプロパティにtriggerDay.toString()の値(=日時情報)を設定することで、トリガーを設定した日時を記録しておき、後で必要なときにそれを参照することができるようになります。

ここまでの処理で実行されるたびに翌日の3時にトリガーが登録されるようになりました。

スクリーンショット 2023-12-17 21.43.58.png

しかし今のままだとスクリプトが実行されるたびにトリガーが登録されるので上記の画像のようにどんどんトリガーが増えて行ってしまいます。そのため実行するときにすでに実行されたトリガーは削除する処理を追加したいと思います。

  var allTriggers = ScriptApp.getProjectTriggers();
  var existingTrigger = null;

  // すでに存在するmyFunctionトリガーを探す
  for (var i = 0; i < allTriggers.length; i++) {
    if (allTriggers[i].getHandlerFunction() === 'myFunction') {
      existingTrigger = allTriggers[i];
      break;
    }
  }

  // すでに存在するトリガーがあれば削除
  if (existingTrigger !== null) {
    ScriptApp.deleteTrigger(existingTrigger);
  }

ScriptApp.getProjectTriggers()を使うことでいま登録されているすべてのトリガーを取得することができます。それを1つずつ見ていきながらgetHandlerFunction()でトリガーの名前を確認し、それが登録したときのmyFunctionと一致した場合はScriptApp.deleteTriggerを使って削除するようにします。

完成品がこちら

GASのコード

function createTrigger() {
  var allTriggers = ScriptApp.getProjectTriggers();
  var existingTrigger = null;

  // すでに存在するmyFunctionトリガーを探す
  for (var i = 0; i < allTriggers.length; i++) {
    if (allTriggers[i].getHandlerFunction() === 'myFunction') {
      existingTrigger = allTriggers[i];
      break;
    }
  }

  // すでに存在するトリガーがあれば削除
  if (existingTrigger !== null) {
    ScriptApp.deleteTrigger(existingTrigger);
  }

  /* ----------------------------
  // ここに定期実行したい処理の内容をかく
  ------------------------------*/

  // 新しいトリガーを作成
  var triggerDay = new Date();
  triggerDay.setDate(triggerDay.getDate() + 1);
  triggerDay.setHours(3);
  triggerDay.setMinutes(0);
  triggerDay.setSeconds(0);

  ScriptApp.newTrigger('myFunction')
    .timeBased()
    .at(triggerDay)
    .create();

  // トリガー設定日時を記録
  var scriptProperties = PropertiesService.getScriptProperties();
  scriptProperties.setProperty('TriggerSetAt', triggerDay.toString());
}

実行結果

スクリーンショット 2023-12-17 21.50.08.png

最後に入れた削除処理のおかげで、今回は何度実行してもトリガーは常に1つしか登録されないようになりました。

トリガーの内容もきちんと翌日の3時ぴったりにスクリプトが実行されるようになっていることが確認できます。

最後に

今日はGASの定期実行を時間指定で行う方法について見ていきました。「GASはピッタリの時間には実行できない」と諦めていた方も、GUIからのトリガー登録よりは少しめんどくさくなりますが、こちらの方法を試してみてはいかがでしょうか。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1