
こんにちは etau です 🤓
「時間主導型」のトリガーといえば Google Apps Script の真骨頂と言っても過言ではありません。
このトリガーは PC が立ち上がってない状況でも、指定した時間や定期的に実行できる、GAS の中で大好きな機能の 1 つです。
この時間型トリガーで 1 点だけトリガーの実行時の正確な日時を取得することに苦慮していました。
最近まで gs ファイルの早い段階で const date = new Date();
を宣言して、その変数を取りまわすなど、いろいろと工夫をこらしていました。
今回、時間主導型のトリガー イベント オブジェクトを利用して、そこから取得する方法に変更しました。
また、記事後半では「オブジェクト指向プログラミングでコードを書く理由」にしたがって、便利な機能をクラス化していきます。

イベント オブジェクトを取得するための準備
myFunction というテスト用の関数を作成します。
テスト関数 myFunction
仮引数としてイベント オブジェクトの e を受け取り、ログ出力する関数です。
function myFunction(e) { console.log(e); }
そして、この関数に対してトリガーを設定します。
トリガーの設定
時間主導型トリガーの イベント オブジェクト をとるためには、myFunction 関数に対して以下のように「イベントソースを選択: 時間主導型」「時間ベースのトリガーのタイプを選択: 分ベースのタイマー」「時間の間隔を選択 (分): 1 分おき」と設定します。

今回はすぐに実行するために、「1 分おき」を選択しています
トリガーを設定した状態で 1 分待ちましょう。
「実行数」を選択すると関数 myFunctionが実行されたことが確認できます。

つづいてログを確認してみましょう。
トリガーイベントの内容
{ month: 2, 'day-of-week': 4, authMode: { toString: [Function: toString], name: [Function: toString], toJSON: [Function: toString], ordinal: [Function: ordinal], compareTo: [Function: compareTo], NONE: { toString: [Function: toString], name: [Function: toString], toJSON: [Function: toString], ordinal: [Function: ordinal], compareTo: [Function: compareTo], NONE: [Circular], CUSTOM_FUNCTION: [Object], LIMITED: [Object], FULL: [Circular] }, CUSTOM_FUNCTION: { toString: [Function: toString], name: [Function: toString], toJSON: [Function: toString], ordinal: [Function: ordinal], compareTo: [Function: compareTo], NONE: [Object], CUSTOM_FUNCTION: [Circular], LIMITED: [Object], FULL: [Circular] }, LIMITED: { toString: [Function: toString], name: [Function: toString], toJSON: [Function: toString], ordinal: [Function: ordinal], compareTo: [Function: compareTo], NONE: [Object], CUSTOM_FUNCTION: [Object], LIMITED: [Circular], FULL: [Circular] }, FULL: [Circular] }, 'day-of-month': 10, timezone: 'UTC', 'week-of-year': 6, minute: 30, hour: 2, second: 8, year: 2022, triggerUid: '10038277' }
今回メインとなるオブジェクト部分をとりだして確認していきます。
今回利用するオブジェクトの内容
今回は authMode, triggerUid プロパティ部分は利用しませんので、必要なものだけピックアップします。
{ month: 2, 'day-of-week': 4, 'day-of-month': 10, timezone: 'UTC', 'week-of-year': 6, minute: 30, hour: 2, second: 8, year: 2022 }
このオブジェクトの内容を確認していきましょう。
取り出したオブジェクトには、以下のプロパティが設定されています。
プロパティ | 値 |
---|---|
timezone | タイムゾーン (UTC しか取得できないため、これ以外の項目があるか不明) |
yaer | 年の値 (4 桁) |
month | 月の値 (1 – 12) |
day-of-month | 日の値 (1 – 31) |
hour | 時間の値 (0 – 23) |
minute | 分の値 (0 – 59) |
second | 秒の値 (0 – 59) |
week-of-year | その年の週数の値 (1 – 52) |
day-of-week | 曜日の値 (月曜日: 1, 火曜日: 2, …, 日曜日: 7) |
ハイフンを含むプロパティは、ブラケット記法で取得する必要があります
日時に関する情報がびっしり詰まってますね。
今回は使いませんが week-of-year
や day-of-week
なんてプロパティがあるのには、驚きました。
また、Date オブジェクトと値の異なる month
や day-of-week
のあつかいには注意が必要です。
さて、目的であるトリガーが実行された日時を返す関数を作っていきます。
イベント オブジェクトから実行時間を取得する関数 getDate
さきほど確認した、プロパティから Date オブジェクトを作って返す関数 getDate を作っていきます。
function myFunction(e) { const date = getDate(e); console.log(date); } /** * 時間主導型のトリガー イベント オブジェクトから、トリガーが実行された日時を取得する関数 * @param {Object} e - 時間主導型のトリガー イベント オブジェクト * @return {Date} トリガーが実行された日時の Date オブジェクト */ function getDate(e) { const date = new Date( e.year, e.month - 1, e['day-of-month'], e.hour, e.minute, e.second ); return date; }
実行した結果のログを確認していきましょう。
ログの確認方法は、同様に [実行数] から [myFunction] をクリックすることで確認できます

開始時間とデバッグで表示されている時間を比較すると、9 時間のズレがあります。

timezone プロパティの値を確認すると UTC のため、このような状況になっています。
timezone プロパティが JST のイベント オブジェクト取得できればとりまわしが楽なので、appsscript.json の設定やトリガー設定時のパラメーターを変更して試してみましたが、UTC 以外の値の取得はできませんでした。
では、実際に利用したい JST 環境の実行時間を取得するメソッドを組み込んだクラスを作成していきます。
クラス化
実装するクラスのメンバー (プロパティとメソッド) について、かんたんに解説します。
メンバーの設定
メンバー種別 | メンバー名 | メンバーの内容 |
---|---|---|
プロパティ | timezone | タイムゾーン (UTC しか取得できないため、これ以外の項目があるか不明) |
プロパティ | yaer | 年の値 (4 桁) |
プロパティ | month | 月の値 (1 – 12) |
プロパティ | date | 日の値 (1 – 31) |
プロパティ | hours | 時間の値 (0 – 23) |
プロパティ | minutes | 分の値 (0 – 59) |
プロパティ | seconds | 秒の値 (0 – 59) |
メソッド | getLocaleDate | 現地時間を取得するメソッド |
メソッド | getDate | イベント オブジェクトから取得したトリガー実行日時を取得するメソッド |
プロパティの数は多くなりましたが、各項目を判定材料に使いそうなので、お許しください。
すべてをプロパティに設定する必要がなければ、利用する値だけを設定してください。
TriggerTimeEvents クラスのコード
前述のメンバーをクラスに設定していきます。
class TriggerTimeEvents { /** * 時間主導型のトリガー イベントに関するコンストラクタ * @constructor * @param {Object} e - 時間主導型のトリガー イベント オブジェクト */ constructor(e) { /** @type {number} */ this.year = e.year; /** @type {number} */ this.month = e.month; /** @type {number} */ this.date = e['day-of-month']; /** @type {number} */ this.hour = e.hour; /** @type {number} */ this.minute = e.minute; /** @type {number} */ this.second = e.second; /** @type {string} NOTE: UTC 以外の値が取れるか不明 */ this.timezone = e.timezone; } /** * 現地時間を取得するメソッド * @param {number} diffHours - 時差 * @return {Date} 時差を調整した日時 * NOTE: this.timezone が UTC でない場合 (JST であると仮定した) の処理あり */ getLocaleDate(diffHours = 9) { if (this.timezone !== 'UTC') return this.getDate(); const date = this.getDate(); date.setHours(date.getHours() + diffHours); return date; } /** * 時間主導型のトリガーが実行された時間を取得するメソッド * @return {Date} 時間主導型のトリガーが実行された日時 * NOTE: 確認されている状況では UTC の値が設定されている */ getDate() { const date = new Date( this.year, this.month - 1, this.date, this.hour, this.minute, this.second ); return date; } }
getLocaleDate メソッドについては timezone プロパティの値が UTC 以外の場合は、JST が取得できる前提でコードを実装しています
呼び出し側の関数で getLocaleDate メソッドで正しく値が取得されているかを確認します。
function myFunction(e) { const tte = new TriggerTimeEvents(e); console.log(tte.getLocaleDate()); }

目的の日時の値が無事取得できています。
GitHub リンク
まとめ
今回は、時間主導型トリガーのイベント オブジェクトから正確な実行日時を取得するためのコードを書いて、クラス化していきました。
このコードを書くきっかけになった時間主導型トリガー「特定の日時」を使ったプログラムについて説明します。
時間主導型トリガー「特定の日時」の設定をある日の「12:00」ピッタリに設定しておくと、その時間にトリガーが実行されます。
問題がおこったプログラムの内容は、スプレッドシートの特定列を参照し、同日同時刻の表記のあるメッセージをチャット ツールにリマインダーとして送信するというものでした。
ある日送られてくるはずのリマインダーの通知がないことに気づき、トリガーの設定とコードを調べましたが、どちらも問題なくエラーも確認できませんでした。
ログから開始時間を確認すると「12:00:59」に実行されていました。
ここからは仮定の話ですが、コードの日時の値を取得するタイミングですでに 12:01 に切り替わっていて、スプレッドシートの同日同時刻を参照して、同じ値がないためリマインダーの通知がされていなかったんでしょう。
こんなことがあり、早いタイミングで日時の値を取得するコードで対処していましたが、それでも上記の状態が再現するのではと、また別の回避用のコードを書き足したりしていました。
今回の実行時間ぴったりの値を取得するこの方法によって、今後はスッキリと対応できると思います。
テスト コードを実行された方は、毎分トリガーの削除をお忘れなく。