プログラミング学習

GASのトリガー毎日定期実行/特定の日時! スプレッドシートの取得!

プログラミング学習

gasの覚書です。

GAS(Google Action Script)のトリガーなどの上限とスクリプトの実行時間

トリガーの上限とスクリプトの実行時間は踏まえて設計する必要がありそうです。この2つを回避する形で考えていくことになりやすいです。

この手のサービスは必ず上限があるため、先に制限を調べるのは大事です。

トリガーの上限が20なので、試しに21個登録したらエラーがでました。単純にトリガーの数という理解で大丈夫そうです。

エラー	
Exception: This script has too many triggers. Triggers must be deleted from the script before more can be added.
setTweet

スクリプトのランタイムは6分制限です。データの読み込みに6分以上かかるとアウトです。

極力、少ない実行数にしましょう。

他の条件もGoogleさんのリファレンスに綺麗にまとまっていました。

スポンサーリンク

GASのスケジュール(登録・削除・取得・管理)

GASにスケジュール登録する

時計のアイコン[トリガー] > 新しいトリガーを作成します > イベントソースを選択[時間主導型]

ただ、大雑把にしか時間指定できないようです。

時間ベースのトリガーのタイプを選択を[特定日時]に変更

管理画面から手動で設定できますけど、GASでプログラムを書いて制御する方法がよさそうです。setTriggerです。

GASのトリガー特定の日時!

setTriggerを実行してみましょう。

初回、このコードを実行するために権限の許可が必要です。

function setTrigger() {
  const date = new Date(2023, 4, 30, 0, 1);
  ScriptApp.newTrigger('tweet').timeBased().at(date).create();
}

時計のアイコン[トリガー]を確認すると、スケジュールに登録されています。

その時間になると、newTriggerのツイートが実行される仕組みです。

登録は20個までの上限があるため、登録したら削除してあげる必要がありそうです。

GASのトリガー毎日・毎時間定期実行

毎時間実行する方法です。毎日は.everyDays(1)ですかね。

function updateSchedule() {

  ScriptApp.newTrigger("scheduleTweet")
    .timeBased()
    .everyHours(1)
    .create();

}

GASのトリガー毎時間定期実行のデメリット

よくみると、1時間おき、2時間おき、4時間おき、6時間おき、8時間おき、12時間おきしかない!

5時間おきに設定したくてもできない。プログラミングからそれ以外の設定を割り当てようとすると、勝手に1時間おきになります。このあたりは中途半端な気がしましたね。

gasのスケジュールを削除する

全トリガーを削除するコードです。

function deleteTrigger() {
  const removeTriggers = ScriptApp.getProjectTriggers();
  for(const removeTrigger of removeTriggers ){
    ScriptApp.deleteTrigger(removeTrigger );
  }
}

JavaScriptの経過日数のサンプル

タイムスタンプを 1000(ミリ秒) × 60(秒) × 60(分) × 24(時間) = 86400000 で割ると経過日数です。Math.floorは小数点切り捨て。

const startDate = new Date('2023-01-01');
const daysPassed = calculateDaysPassed(startDate);
console.log('経過日数:', daysPassed);

function calculateDaysPassed(startDate) {
  const now = new Date();
  const timeDifference = now.getTime() - startDate.getTime();
  const daysPassed = Math.floor(timeDifference / (1000 * 60 * 60 * 24));
  return daysPassed;
}
スポンサーリンク

GASのトリガー

GASでトリガーを取得するサンプル

eventオブジェクトを受け取れる。

function schedule(event) {
  const triggerUid = event.triggerUid;
  const trigger = ScriptApp.getProjectTriggers().find(trigger => trigger.getUniqueId() === triggerUid);
}

ただ、gasは取得できる状態が限られているようです。詳細時間が取得できないっぽいです。この点は残念で改善してもらえるとよい気もしますね。

GASでトリガーを取得してログを確認する方法

普通に実行するとこうなる!まあ、eventが入っていないので当たり前という気がする。

TypeError: Cannot read properties of undefined (reading 'triggerUid')

eventの実行ログを確認する方法はトリガーから行わないとダメなようだ。

トリガーの実行数から確認するとよいです。

トリガー > 縦3点 > 実行数

[トリガーを追加]からデバッグ用のトリガーを作るか、普通に登録して管理画面から時間調整とかでしょうか。

デバッグ用のトリガーの作り方。

トリガー > トリガーを追加

  • eventを受け取る関数
  • 時間主導型
  • 分ベースのタイマー
  • 1分おき(すぐデバッグできるように)

GASの弱点を克服するサンプルコード

GASはトリガーから詳細時間を取得できませんからスクリプトプロパティによる応用を考える気がします。。

その対策としてstackoverflowのこのコードは素晴らしい!

ARGUMENTS_KEY = "arguments"
  • setする際にargumentsを識別子に使う
  • setする際にオブジェクトとして格納する
  • getする際にイベントオブジェクトで取得する

脱初心者向けのコードですね。個人的にも応用させてもらったのでありがとうございます。

new Date(year, month – 1, day, hour, minute);

忘れないように注意。

new Date(year, month - 1, day, hour, minute);

JavaScriptでは、Dateオブジェクトを扱う際に月は0から始まります。つまり、1月は0、2月は1、12月は11となります。

なぜJavaScriptのDateオブジェクトは月を0から数えるのですか?1月を0月と表現すると、バグを誘発しないでしょうか?

https://jp.quora.com/%E3%81%AA%E3%81%9CJavaScript%E3%81%AEDate%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AF%E6%9C%88%E3%82%920%E3%81%8B%E3%82%89%E6%95%B0%E3%81%88%E3%82%8B%E3%81%AE%E3%81%A7%E3%81%99%E3%81%8B-1%E6%9C%88

GASでタイムゾーンUTCで時間がずれる

次のコードでUTC(協定世界時)の概要がわかる。

  const date = new Date(year, month - 1, day, hour, minute);
  console.log(`Local Date: ${date}`);

  const utcDate = new Date(Date.UTC(year, month - 1, day, hour, minute));
  console.log(`UTC Date: ${utcDate}`);
  console.log(`UTC Date: ${utcDate.toISOString()}`);

ただし、GASは内部的にはすべてUTCで日時を管理しているっぽいです。GASのみならあまり使うことないかもですね。

むしろ問題になるのは、JavaScriptのDateオブジェクトをJSON文字列に変換すると、その日付と時刻はUTCで表されます。こうなると、混乱しやすいのでJSON.stringify()する前に文字列に変換する必要がありそうだ。

propertiesObj.triggerTime = propertiesObj.triggerTime.toString();

UTCの時間は日本時間から9時間をひいた数字。JSON.stringify()を使ったのち、スクリプトプロパティに保存ずると9時間ずれてしまいます。UTCの時間になっただけだから別にいいんだけど、デバッグする際に紛らわしいため文字列に変換した方が安心感があります。

スポンサーリンク

GAS(Google Action Script)でスプレッドシートのスケジュール重複チェック

function createDateTime() {
  const sheetName = "schedule";
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
  const lastRow = sheet.getLastRow();

  const scheduledDates = []; // 実施日を格納する配列

  // ... (省略) ...

  for (let i = 0; i < noValues.length; i++) {
    // ... (省略) ...

    // 実施日を取得
    const scheduledDateTime = new Date(date + ' ' + time);

    // 実施日が配列内で重複しているかチェック
    if (scheduledDates.includes(scheduledDateTime.toString())) {
      console.log('同じ年月日時刻のスケジュールが存在するため、登録をスキップします。');
      continue;
    }

    // 実施日を配列に追加
    scheduledDates.push(scheduledDateTime.toString());

    // ... (省略) ...

    setScheduleTrigger(scheduledDateTime);
  }
}

次のような書き方もできるが、あまりパフォーマンスがよくない。基本for文を使う方法がシンプルイズベスト。

const duplicateIndex = findDuplicateSchedule(scheduledDateTime, i, dateValues, timeValues);
if (duplicateIndex !== -1) {
  console.log('同じ年月日時刻のスケジュールが存在するため、登録をスキップします。');
  continue;
}

function findDuplicateSchedule(scheduledDateTime, currentIndex, dateValues, timeValues) {
  for (let i = 0; i < currentIndex; i++) {
    const date = dateValues[i][0];
    const time = timeValues[i][0];
    const dateTime = new Date(date + ' ' + time);
    if (dateTime.getTime() === scheduledDateTime.getTime()) {
      return i;
    }
  }
  return -1;
}
スポンサーリンク

GAS(Google Action Script)でスプレッドシートのAPI

便利だったものめもです。ハイパーリンクを設定するときに便利です。複数枚可。

setLinkUrl(startOffset, endOffset, linkUrl)

https://developers.google.com/apps-script/reference/spreadsheet/rich-text-value-builder?hl=ja#setlinkurlstartoffset-numeric,-endoffset-numeric,-url-string

newRichTextValue() とsetRichTextValue(richTextValue):とあわせて使うとよいです。

var richText = SpreadsheetApp.newRichTextValue()
    .setText(“Hello world”)
    .setTextStyle(0, 5, bold)
    .build();
range.setRichTextValue(richText);

https://developers.google.com/apps-script/reference/spreadsheet/range?hl=ja#setrichtextvaluerichtextvalue

ご参考になれば幸いです。

コメント

タイトルとURLをコピーしました