GASのセルを直したら、更新日時を自動で入れる

GAS

※本記事はプロモーションを含みます

目次

スプレッドシートで管理している業務台帳やタスクリストで、GASのセルを直したら、更新日時を自動で入れる仕組みを作りたい場面は非常に多いです。手動で日付を入力させようとすると、入力漏れや入力ミスが発生するので、不具合を完全に防ぐことは不可能です。「GAS onEdit 更新日時 自動記入」という仕組みを導入すれば、担当者がセルを書き換えた瞬間にバックグラウンドで最新の時刻が記録されるようになり、データの鮮度や変更履歴の追跡が格段に楽になります。ただし、単純なスクリプトを設置しただけでは、ヘッダー行を編集した時に日付が上書きされたり、関係ないシートのデータまで書き換わってしまうトラブルが起きます。なので、この記事では、現場の業務スプレッドシートで安全に更新日時を記録するための書き方と、運用時に突き当たる細かなズレや回避策をメモとしてまとめます。

編集した行の端っこに最終更新日時を残したい場面

スプレッドシートを使った共同作業では、いつ誰がどの行を更新したのかという情報が、業務の進行に直接関係してきます。たとえば、顧客の対応ステータスを管理するシートや、在庫の入荷状況を更新するリストでは、データの値そのものと同じくらい「それがいつ変更されたか」という最終更新日時が重要です。手動で日付を入力させると、どうしても記入忘れが起きます。なので、ショートカットキーによる日付入力を行っても、時刻まで記録されなかったり、操作の手間が増えて入力自体が敬遠される原因になります。

そこで、GASのシンプルトリガーであるonEdit関数を活用し、ユーザーがセルを書き換えたタイミングで、その行の指定した列へ自動的に日時を書き込む仕組みを組み込みます。これを使えば、入力担当者は通常のデータ入力作業だけに集中できるので、システム側が裏で正確な日時を記録してくれます。ただし、スプレッドシート全体に対して無条件で動くスクリプトを書いてしまうと、意図しない場所のデータが勝手に書き換わる事故につながります。というのは、実務で使うためには、単に現在時刻を取得してセットするだけでなく、どのシートのどの行が編集されたかを適切に判定する条件分岐が不可欠だからです。

シンプルトリガーで動かす最小限のスクリプトと判定条件

入力シートのセルが直されたときに、その行の5列目(E列)へ自動的に現在時刻を書き込みたい場合に使用する最小限のスクリプトです。編集イベントが発生した際に渡されるイベントオブジェクトを利用して、変更されたシートや行番号を判定します。

function onEdit(e) { // セルが直されたときに動く関数を準備します。パラメータeに編集情報が入ります。
  const sheet = e.range.getSheet(); // 編集されたセルが存在するシートを取得します。
  if (sheet.getName() !== "入力") return; // シート名が「入力」ではない場合、ここで処理を終了します。
  const row = e.range.getRow(); // 編集されたセルの行番号を取得します。
  if (row === 1) return; // 1行目の見出し行が直された場合、ここで処理を終了します。
  sheet.getRange(row, 5).setValue(new Date()); // 対象行の5列目(E列)に現在日時を入力します。
}

このコードでは、関数名にonEditを使用しているため、特別な設定を行わなくてもファイルを保存した時点でシンプルトリガーとして機能し始めます。ユーザーがセルを編集すると、GASは引数eに編集箇所の情報(イベントオブジェクト)を格納して処理を実行します。コード内では、まず編集されたセルが含まれるシートオブジェクトを取得します。なので、シート名が「入力」でない場合は、returnで処理を即座に終了させています。さらに、編集された行番号を取得します。というのは、それが見出し行である1行目だった場合も、処理をスキップするように条件を設けているからです。条件を通過した行に対してのみ、sheet.getRange(row, 5).setValue(new Date())によって5列目に現在日時を書き込んでいます。

この構成で運用を始めた際に生じる最初のズレは、更新日時列そのものを編集した時や、全く関係のない列(例えばメモ欄や計算用の列)を直した時にも、更新日時が最新に塗り替えられてしまう点です。ただし、1行目の見出しは除外できていても、特定の入力列(例えばステータス列)だけが変更された時だけ日時を更新したいという要望には対応できません。このズレを回避するには、さらにe.range.getColumn()を使って、編集された列が更新対象の列かどうかをチェックする判定文を追加する必要があります。なので、例えばif (e.range.getColumn() !== 3) return;のように記述することで、C列が書き換わった時のみE列に更新日時を入れるという厳密な制御が可能になります。

大量データの貼り付けや一括削除で起きる挙動のズレ

実務の現場では、ユーザーが1つのセルを順番に手入力するだけではありません。なので、他のファイルからコピーした複数行のデータを一括で貼り付けたり、不要になった行のデータをまとめてDELキーで削除したりする操作が日常的に行われます。単一のセル編集を前提としたロジックのまま一括操作を行うと、想定外の挙動やデータの記録漏れが発生します。

イベントオブジェクトe.rangeは、ユーザーが選択して変更を加えたすべての範囲を保持しています。複数行や複数列を一度に貼り付けた場合、e.range.getRow()が返すのは、選択範囲の左上にある先頭の行番号だけです。なので、先ほどのスクリプトのままだと、10行分のデータをまとめて貼り付けたとしても、最初に編集された1行目のE列にしか更新日時が記入されません。というのは、残り9行の更新日時が古いまま残ってしまうというズレが発生するからです。

この問題を回避するためには、編集された範囲の行数(e.range.getNumRows())を取得し、ループ処理によって変更されたすべての行に対して更新日時を反映させる設計が必要です。あるいは、範囲全体に対して一括で値をセットするsetValuesを活用し、変更があった行の更新日時列を一度に書き換える実装を行うことで、一括処理による更新漏れを防ぐことができます。ただし、セルの中身を一括削除した際にもonEditは発火します。なので、値が空になった場合(削除操作)には更新日時を消去するのか、それとも削除された日時として現在時刻を残すのかというルールも、業務の運用に合わせて事前に決めておきましょう。

数式による変化や別ツールからの書き換えで発火しない制限

スプレッドシートで更新日時を自動で入れる仕組みを運用する上で、最も多くの人が直面する仕様上の壁が、onEditは人間が画面上で直接セルを操作・編集した時にしか発火しないという制限です。

例えば、あるセルにIMPORTRANGEやVLOOKUPといった数式が入っており、参照元のデータが変わったことで画面上の表示値が更新されたとします。ただし、GASのonEditトリガーは一切反応しません。なので、Googleフォームからの自動転記によって新しい行が追加された場合や、Pythonや他のGASスクリプトによってセルが書き換えられた場合も同様に、シンプルトリガーは発火しません。というのは、これを、セルが変わったのに日時が更新されない不具合だと勘違いしてしまうケースが多発するからです。

このズレを回避するためのアプローチは2つあります。1つ目は、他のGASスクリプトや外部連携で値を書き換える処理がある場合、その処理側のコードの中に更新日時列に日付を入れる処理を明示的に組み込むことです。2つ目は、数式による変更やフォーム転記の検知を行いたい場合に、シンプルトリガーのonEditではなく、インストーラブルトリガーのonChange(変更時トリガー)を使用することです。ただし、onChangeはシート構造の変更や特定の自動更新に対してもトリガーされます。なので、イベントオブジェクト構造がonEditとは異なり、編集された具体的な行や列の取得が複雑になります。用途に応じた慎重な仕組み構築を確認しましょう。

タイムスタンプの表示形式とタイムゾーンのズレに対処する

スプレッドシートのセルに対してsetValue(new Date())を実行すると、JavaScriptのDateオブジェクトがそのままシートに渡されます。このとき、画面上にどのように日時が表示されるかは、スプレッドシート側の表示形式設定や、スプレッドシートおよびGASプロジェクトのタイムゾーン設定に強く影響を受けます。

よくあるトラブルとして、GAS側で取得した現在時刻が標準時(UTC)として解釈され、スプレッドシート上で9時間前の時刻が表示されてしまう現象があります。というのは、Google Cloud Project側のタイムゾーン設定がAsia/Tokyoになっていない場合や、スプレッドシートのプロパティ設定でタイムゾーンがズレている場合に発生するからです。ただし、セルには正確な日時が入っているものの、セルの表示形式が「年/月/日」になっているために、時刻が表示されず、一見すると更新時刻が記録されていないように見えるケースもあります。

このズレを確実に回避するには、2つの方法があります。もっとも確実なのは、GAS側で文字列として整形してから書き込む方法です。Utilities.formatDate(new Date(), Session.getScriptTimeZone(), “yyyy/MM/dd HH:mm:ss”)を使用し、フォーマット済みの文字列を作ってからsetValueに渡せば、スプレッドシートの表示形式設定に左右されず、意図通りの日時テキストを保持できます。ただし、文字列として書き込んだ場合、後からスプレッドシートの機能で日付順に並び替えたり、日付関数で計算したりする際に支障が出る場合があります。なので、日時データをシリアル値(日付型)のまま保持したい場合は、setValue(new Date())で書き込んだ上で、GASのsetNumberFormat(“yyyy/MM/dd HH:mm:ss”)メソッドを使って、セル側の表示形式をプログラムから強制的に指定する構成をとるのが安全です。設定方法を確認しましょう。

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