本サイトはアフィリエイト広告(A8.net/もしもアフィリエイト/アクセストレード等)を利用しています
GASで毎月1日に月次シートを自動複製する仕組み
📂 スプレッドシート

GASで毎月1日に月次シートを自動複製する仕組み

📅 ⏱ 読了 約8分 ✍️ 凛

💡 本記事には広告(アフィリエイトリンク)が含まれる場合があります。

凛です。

我が家では家計簿をGoogleスプレッドシートでつけているのですが、毎月1日になると「今月分のシート」を手作業で用意するのが地味に面倒でした。前月のシートを右クリックして「コピーを作成」、名前を「2026-08」みたいに直して、いらない前月の数字を消して……。夜勤明けの眠い頭でこれをやると、たまに名前を間違えたり、前月分をそのまま上書きしそうになってヒヤッとしたり。

ある月、うっかりコピーを作り忘れて前月シートに8月分を打ち込んでしまい、後から泣きながら仕分け直したことがありました。「これ、GASにやらせればいいのでは?」と思い立って作ったのが、今回ご紹介する「毎月1日にテンプレートを自動で複製する仕組み」です。作ってからは毎月1日の朝、勝手に真っさらな今月シートが用意されているので、本当に気が楽になりました。

GASで毎月1日に月次シートを自動複製する仕組み

この記事では、テンプレートシートを1枚だけ用意しておき、それを毎月1日の朝に自動で複製して「2026-08」のような月次シートを作る仕組みを、初心者の方でも真似できるように順を追って説明します。ただ複製するだけでなく、二重に作られない工夫(冪等化)や、古いシートを整理するヒントまでカバーします。

全体像:何を作るのか

まずはゴールを整理しておきます。細かいコードに入る前に「全体で何が起きるのか」を頭に入れておくと、後の説明がぐっと分かりやすくなります。

完成したときの動き

作るのは、次のような流れで動く仕組みです。

  1. スプレッドシートに _template という「ひな形シート」を1枚だけ置いておく
  2. 毎月1日の朝、GASが自動で起動する
  3. _template をコピーして、名前を「2026-08」のように当月へ書き換える
  4. 同じ名前のシートが既にあれば、何もしない(二重作成の防止)

つまり私たちは毎月何もしなくてよくて、月が変わると勝手に新しいシートが増えていく、という状態を目指します。

使う主なメソッド

今回の主役になるメソッドを先に紹介しておきます。名前だけ眺めておいてください。

メソッド役割
sheet.copyTo(スプレッドシート)シートを同じファイル内に複製する
Utilities.formatDate(日付, タイムゾーン, 書式)日付を「2026-08」のような文字列に整形する
sheet.setName(名前)シートの名前を変更する
ss.getSheetByName(名前)名前でシートを探す(無ければ null
ss.moveActiveSheet(位置)アクティブなシートを指定位置へ移動する
sheet.hideSheet()シートを非表示にする

これらを組み合わせるだけなので、難しい前提知識は要りません。順番に組み立てていきましょう。

テンプレートシートを1枚用意する

自動化の土台になるのが「ひな形(テンプレート)」です。ここを丁寧に作っておくと、毎月きれいなシートが手に入ります。

ひな形の作り方

スプレッドシートを開いて、下のシートタブから新しいシートを1枚追加し、名前を _template にします。私は頭にアンダースコアを付けて「これは特別なシートですよ」と自分に分かるようにしています。

このシートに、毎月使いたい見出しや表の枠、計算式などをあらかじめ書き込んでおきます。家計簿なら「日付・費目・金額・メモ」といった見出しの行や、合計を出すSUM式などですね。ここに入れておいた内容が、そのまま毎月コピーされます。

コピーが末尾に増えることを知っておく

ここで一つ、GASのクセを覚えておいてください。copyTo でシートを複製すると、新しいシートは「_template のコピー」という名前で、シートの**一番うしろ(末尾)**に追加されます。自分で好きな名前を付けてくれるわけではありません。

なので実際のコードでは「コピーを作る → その直後に名前を付け替える」という2ステップで進めます。この性質を知らないと「あれ、名前が変なんだけど?」と戸惑うので、先に頭に入れておくと安心です。

当月の名前を作って複製する

いよいよコードの中心部分です。まずは「今月の名前を作る」ところと「テンプレートを複製して名前を付ける」ところを書いてみます。

当月の名前を組み立てる

「2026-08」のような当月の文字列は、Utilities.formatDate で作ります。タイムゾーンを必ず Asia/Tokyo と指定するのがポイントです。ここを空にすると、実行環境によっては日付がずれて、月初なのに前月扱いになってしまうことがあります。

// ※構文・API仕様を確認済み(GAS V8ランタイム向け)

function getCurrentMonthName() {
  // 今日の日付を取得する
  var today = new Date();
  // 日本時間で「yyyy-MM」(例:2026-08)の形に整える
  var name = Utilities.formatDate(today, 'Asia/Tokyo', 'yyyy-MM');
  // 出来上がった名前を返す
  return name;
}

この関数を実行すると、8月であれば 2026-08 という文字列が返ってきます。まずはこれ単体で動かして、正しい月名が出るか確かめてみると安心です。

テンプレートを複製して名前を付ける

次に、テンプレートを複製して当月名を付ける本体です。

// ※構文・API仕様を確認済み(GAS V8ランタイム向け)

function createMonthlySheet() {
  // 今アクティブなスプレッドシート(このファイル)を取得する
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  // ひな形シートを名前で取得する
  var template = ss.getSheetByName('_template');

  // 万が一テンプレートが無ければ、ここで止めて知らせる
  if (!template) {
    throw new Error('_template という名前のシートが見つかりません');
  }

  // 当月の名前(例:2026-08)を作る
  var name = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy-MM');

  // テンプレートを同じファイル内に複製する(末尾に「_template のコピー」が追加される)
  var newSheet = template.copyTo(ss);
  // 追加されたシートの名前を当月名に変更する
  newSheet.setName(name);

  // ログに出して、あとで実行履歴から確認できるようにする
  Logger.log(name + ' を作成しました');
}

これだけで「テンプレートを複製して当月名を付ける」という基本動作は完成です。ただし、このままだと手動でもう一度実行したり、トリガーが二重に動いたりすると、同じ月のシートが2枚できてしまいます。次でそこを直します。

同じ月のシートを二度作らない(冪等化)

自動化で一番こわいのが「気づかないうちに重複が増えていた」というトラブルです。ここを固めておくと、安心して放置できます。

getSheetByName でチェックする

対策はシンプルで、「作る前に、その名前のシートが既にあるか確認する」だけです。ss.getSheetByName(name) は、シートがあればそのシートを、無ければ null を返します。この性質を使って、無いときだけ作るようにします。

// ※構文・API仕様を確認済み(GAS V8ランタイム向け)

function createMonthlySheetSafe() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var template = ss.getSheetByName('_template');
  if (!template) {
    throw new Error('_template という名前のシートが見つかりません');
  }

  // 当月の名前を作る
  var name = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy-MM');

  // 既に同じ名前のシートがあるか確認する
  var existing = ss.getSheetByName(name);
  if (existing) {
    // もうあるなら、何もしないで終了する(重複を防ぐ)
    Logger.log(name + ' は既にあるので作成をスキップしました');
    return;
  }

  // まだ無いときだけ複製して名前を付ける
  var newSheet = template.copyTo(ss);
  newSheet.setName(name);
  Logger.log(name + ' を新しく作成しました');
}

このように「あれば作らない」という作りにしておくと、同じ関数を何回実行しても結果が変わりません。これを**冪等(べきとう)**と呼びます。難しい言葉ですが、「何度押しても安全なボタン」くらいの意味だと思ってください。

なぜ冪等化が大事なのか

時間主導トリガーは、ごくまれに同じタイミングで2回動くことがあります。また「ちゃんと動いたかな?」と心配になって、自分で手動実行してしまうこともありますよね。冪等化してあれば、こうした二重起動でもシートが1枚しか作られないので、後片付けの手間がゼロになります。自動化では「間違って複数回動いても壊れない」ことがとても大切です。

シートの並び順とテンプレートの隠し方を整える

動くようになったら、次は「使いやすさ」の仕上げです。作った月次シートを見つけやすくし、ひな形は目立たないようにします。

新しいシートを先頭に移動する

新しく作ったシートは末尾にできるので、毎回一番左まで探しに行くのは面倒です。作った直後に先頭へ移動しておくと、開いてすぐ今月のシートに触れます。

// ※構文・API仕様を確認済み(GAS V8ランタイム向け)

function createAndReorder() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var template = ss.getSheetByName('_template');
  var name = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy-MM');

  // 既にあればスキップ(冪等化)
  if (ss.getSheetByName(name)) {
    Logger.log(name + ' は既にあります');
    return;
  }

  // 複製して名前を付ける
  var newSheet = template.copyTo(ss);
  newSheet.setName(name);

  // 作ったシートをアクティブ(選択状態)にする
  ss.setActiveSheet(newSheet);
  // アクティブなシートを1番目(先頭)へ移動する
  ss.moveActiveSheet(1);

  Logger.log(name + ' を作成して先頭に移動しました');
}

moveActiveSheet(1)1 は「1番目」という意味で、先頭を指します。ここを 2 にすれば2番目に置けます。

テンプレートを非表示にする

ひな形の _template は普段は触らないので、非表示にしておくとタブがすっきりします。hideSheet() を一度呼べば隠せます。

// ※構文・API仕様を確認済み(GAS V8ランタイム向け)

function hideTemplate() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var template = ss.getSheetByName('_template');
  if (template) {
    // ひな形シートを非表示にする
    template.hideSheet();
    Logger.log('_template を非表示にしました');
  }
}

もう一度編集したくなったら、シートタブを右クリックして表示に戻せます。隠しても中身は消えないので安心してください。

応用:当月の日付を書き込み、前月から繰り越す

ここからは少しだけ発展編です。単にコピーするだけでなく、「今月の情報を書き込む」「前月の数字を引き継ぐ」といった一手間を加えると、ぐっと実用的になります。

当月の日付欄を埋める

テンプレートの決まったセル(例えばA1)に「対象月」を書き込んでおくと、どのシートが何月分か一目で分かります。

// ※構文・API仕様を確認済み(GAS V8ランタイム向け)

function createWithMonthLabel() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var template = ss.getSheetByName('_template');
  var name = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy-MM');

  if (ss.getSheetByName(name)) {
    return; // 既にあれば何もしない
  }

  var newSheet = template.copyTo(ss);
  newSheet.setName(name);

  // A1セルに「2026年08月」のような表示用の月ラベルを書き込む
  var label = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy年MM月');
  newSheet.getRange('A1').setValue(label);

  Logger.log(name + ' を作成し、月ラベルを設定しました');
}

前月シートから繰越値をコピーする

家計簿で「前月末の残高を今月の頭に引き継ぎたい」といった場合は、前月シートの決まったセルから値を読み取って、今月シートに書き込みます。前月の名前は、日付をひと月戻して同じ書式で作れば求められます。

// ※構文・API仕様を確認済み(GAS V8ランタイム向け)

function carryOverBalance() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var name = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy-MM');
  var current = ss.getSheetByName(name);
  if (!current) {
    return; // 今月シートがまだ無ければ何もしない
  }

  // 今日から1か月前の日付を作る(前月シート名の算出用)
  var prevDate = new Date();
  prevDate.setMonth(prevDate.getMonth() - 1);
  var prevName = Utilities.formatDate(prevDate, 'Asia/Tokyo', 'yyyy-MM');

  // 前月シートを取得する
  var prev = ss.getSheetByName(prevName);
  if (!prev) {
    Logger.log('前月シート(' + prevName + ')が無いので繰越しません');
    return;
  }

  // 前月のB10セル(例:月末残高)の値を読み取る
  var balance = prev.getRange('B10').getValue();
  // 今月のB1セル(例:繰越額)へ書き込む
  current.getRange('B1').setValue(balance);

  Logger.log(prevName + ' から ' + name + ' へ残高を繰り越しました');
}

セルの位置(B10B1)は、ご自身のテンプレートに合わせて変えてください。setMonth で月を1つ戻すやり方は、1月から12月へまたぐケースも自動で計算してくれるので便利です。

毎月1日の朝に自動実行する(トリガー設定)

コードが手動で正しく動くのを確認したら、いよいよ自動化です。ここで「時間主導トリガー」を設定します。

画面から設定する手順

GASエディタの左側にある時計のアイコン(トリガー)を開き、右下の「トリガーを追加」を押します。次のように設定します。

設定項目選ぶ値
実行する関数createAndReorder(自動で動かしたい関数)
イベントのソース時間主導型
時間ベースのトリガーのタイプ月ベースのタイマー
1日
時刻午前6時〜7時(お好みで)

これで「毎月1日の朝、指定した関数が動く」状態になります。GASのトリガーは「6時〜7時のあいだ」のように幅を持って実行されるので、きっちり6時0分ではない点だけ覚えておいてください。月次シートの作成なら、その程度のズレはまったく問題ありません。

コードでトリガーを作る場合

画面からではなくコードで登録したい場合は、次のように書けます。すでに同じトリガーがある状態で何度も実行すると重複するので、一度だけ実行するのがコツです。

// ※構文・API仕様を確認済み(GAS V8ランタイム向け)

function setupMonthlyTrigger() {
  // createAndReorder を、毎月1日の午前6時台に動かすトリガーを作る
  ScriptApp.newTrigger('createAndReorder')
    .timeBased()          // 時間主導型にする
    .onMonthDay(1)        // 毎月1日に実行する
    .atHour(6)            // 午前6時台に実行する
    .create();            // 実際にトリガーを作成する

  Logger.log('毎月1日のトリガーを登録しました');
}

onMonthDay(1) が「毎月1日」、atHour(6) が「6時台」を意味します。この関数は一度だけ実行してください。何度も実行すると同じトリガーが増えてしまいます。

つまずきやすいポイントと回避策

私が実際にハマった失敗と、その乗り越え方をまとめておきます。同じところで悩まずに済むはずです。

タイムゾーンを指定し忘れて前月になる

最初にやりがちなのが、Utilities.formatDate でタイムゾーンを空にしたり、実行環境のずれで日付が前日扱いになるケースです。特に月初の深夜〜早朝に動かすと、指定を間違えていると「8月1日なのに 2026-07 ができてしまう」ことがあります。必ず 'Asia/Tokyo' を指定し、トリガーの時刻も深夜0時ちょうどではなく朝6時など少し余裕をもたせると安全です。

冪等化を忘れて重複が増える

前述のとおり、手動実行やトリガーの二重起動で同じ月のシートが2枚できることがあります。必ず getSheetByName で存在チェックを入れて、「あれば作らない」形にしておきましょう。私はこれを入れる前、テスト中に「2026-08 のコピー」みたいなシートを量産してしまい、後で削除する羽目になりました。

いきなりトリガー化してテストしづらい

コードが完成していないのにトリガーだけ先に設定すると、思った動きにならなかったときに原因が分かりにくくなります。まずは手動実行(エディタの実行ボタン)で何度か試し、正しい名前・正しい位置にシートができることを確認してから、最後にトリガーを付けるのがおすすめです。

シートが増えすぎてファイルが重くなる

毎月増え続けると、1〜2年でシートが数十枚になり、ファイルの動作が重くなってきます。対策として、古い月を別ファイルへ退避(アーカイブ)する運用が有効です。template.copyTo(別のスプレッドシート) は別ファイルへのコピーもできるので、これを使って古いシートを保管用ファイルへ移し、元ファイルからは ss.deleteSheet(古いシート) で削除する、といった仕組みを年に一度動かすと元ファイルを軽く保てます。削除は取り消しづらいので、アーカイブ先へコピーできたことを確認してから消すようにしてください。

まとめ

毎月1日にテンプレートを複製して月次シートを自動生成する仕組みを、順を追って組み立ててきました。要点を振り返ります。

  • template.copyTo(ss) でひな形を複製し、末尾にできたシートを setName で当月名に変える
  • 当月名は Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy-MM') で作る
  • getSheetByName で存在チェックして「あれば作らない」冪等化を入れる
  • 作ったシートを先頭へ移動し、テンプレートは非表示にして使いやすく整える
  • 月ベースの時間主導トリガーを毎月1日・朝に設定する
  • まず手動で試してからトリガー化し、増えすぎたら古い月をアーカイブする

一度作ってしまえば、あとは毎月勝手に今月のシートが用意されます。私は「もう名前を間違えない」というだけで、心の負担が本当に減りました。ぜひご自身の家計簿や記録シートで試してみてください。

自分でも作れるようになりたい方へ

「こういう小さな自動化を自分でも書けるようになりたい」と思った方は、GASの基礎を一度きちんと押さえておくと、応用がぐっと効くようになります。私自身、看護師をしながら独学で少しずつ書けるようになりました。難しく見えても、今日のコードのように一歩ずつ組み立てれば大丈夫です。焦らず、動くものを一つ作る成功体験から始めてみてください。

Dive into Code(未経験からエンジニアを目指すプログラミングスクール)

関連記事

この記事を書いた人:凛

2児2児のママで現役ナース。夜勤明けの細切れ時間を副業GASに投じ、月5〜8万円の副収入を継続中。「看護師でもコードは書ける」を合言葉に、家事育児とプログラミングを両立する等身大の情報を発信しています。

掲載コードは構文とAPI仕様を確認して載せていますが、お使いの環境に合わせて調整してください。