GASでスプレッドシートからGoogleカレンダーにスケジュールを登録したい！

# GASでスプレッドシートからGoogleカレンダーにスケジュールを登録したい！
スプレッドシートからスケジュールを読み取って、Googleカレンダーに登録するGASを作成しました。
ある程度コピペでも使えると思います。
各環境に応じて書き換えて使ってみてください。

**●前提知識**
スプレッドシートやGASの始め方は[こちら](https://qiita.com/park3taro/items/c20daa229b1be4353765)
GASでカレンダーを登録・削除する方法は[こちら](https://qiita.com/park3taro/items/e33d3bd5381e8ebc6225)
を参考にしてください。


# やりたいこと

下図のようなスプレッドシートの予定表を取得してGoogleカレンダーに登録したい。（内容はフィクションです。）

![](https://paper-attachments.dropbox.com/s_7C74051E044841733087B6D19178BB7C60679FCE850F1AABBCD01B6F3C485358_1601213309592_+2020-09-27+22.27.52.png)


時刻が記入されていないイベントについては終日イベントとして取り込みます。

# サンプルコード
```javascript
function makeCalendar() {
  
  // デフォルトアカウントのGoogleカレンダーを取得する。
  var cal = CalendarApp.getDefaultCalendar();
  
  // アクティブなシートを取得する。
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  
  // スプレッドシートの情報を指定
  const startRow = 2; // 開始行
  const startCol = 1; // 開始列
  const numCol = 9;  // 列数
  
  // 最終行を取得し、行数を取得する。
  var numRow = sheet.getLastRow() - startCol;
  
  // 各行の設定(0始まり)
  const titleCol = 0;
  const startDayCol = 1;
  const startTimeCol = 2;
  const endDayCol = 3;
  const endTimeCol = 4;
  const descriptionCol = 5;
  const locationCol = 6;
  const guestsCol = 7;
  const sendInvitesCol = 8;
  
  // イベント一覧を取得する。
  var events = sheet.getRange(startRow, startCol, numRow, numCol).getValues();
  
  // 取得したリストから順にカレンダーに登録していく。
  for (var i = 0; i < numRow; i++) {
    
    var startTime;
    var endTime;
    
    // [1]終日イベントかどうかの判定(開始時間の有無で判定)
    if (events[i][startTimeCol].length == 0) {
      // 終日イベントの場合（開始時間が未入力の場合終日イベントとする。）
      
      // 開始日をフォーマットする。
      startTime = new Date(events[i][startDayCol]);
      
      // [2]イベント日数が日を跨ぐかどうかの判定。
      if (events[i][endDayCol].length == 0) {
        // 終了日がない場合（1日の終日イベント）
        
        // カレンダーに登録する。
        cal.createAllDayEvent(events[i][titleCol], startTime,
                      {
                        description:events[i][descriptionCol],
                        location:events[i][locationCol],
                        guests:events[i][guestsCol],
                        sendInvites:events[i][sendInvitesCol] == "要" ? true :false
                    });
      } else {
        // 終了日がある場合（複数日のイベント）
        
        // 終了日をフォーマットする。
        endTime = new Date(events[i][endDayCol]);
        // [2]終日イベントで、開始日と終了日が異なる場合、終了日を1日遅らせる必要がある。
        endTime.setDate(endTime.getDate() + 1);
        
        // カレンダーに登録する。
        cal.createAllDayEvent(events[i][titleCol], startTime, endTime,
                      {
                        description:events[i][descriptionCol],
                        location:events[i][locationCol],
                        guests:events[i][guestsCol],
                        sendInvites:events[i][sendInvitesCol] == "要" ? true :false
                    });
        
      };
    } else {
      // 終日イベントでない場合
      
      // [3]日付と時刻を結合し、フォーマットする。
      startTime = new Date(Utilities.formatDate(events[i][startDayCol], "Asia/Tokyo", "yyyy/MM/dd") + " " +
                            Utilities.formatDate(events[i][startTimeCol], "Asia/Tokyo", "HH:mm:ss"));
      endTime = new Date(Utilities.formatDate(events[i][endDayCol], "Asia/Tokyo", "yyyy/MM/dd") + " " +
                          Utilities.formatDate(events[i][endTimeCol], "Asia/Tokyo", "HH:mm:ss"));
      
      // カレンダーに登録する。
      cal.createEvent(events[i][titleCol], startTime, endTime, 
                      {
                        description:events[i][descriptionCol],
                        location:events[i][locationCol],
                        guests:events[i][guestsCol],
                        sendInvites:events[i][sendInvitesCol] == "要" ? true :false
                      });
      
    };
  };
}
```

上記のコードを取り込んでGASを実行すると、

![](https://paper-attachments.dropbox.com/s_7C74051E044841733087B6D19178BB7C60679FCE850F1AABBCD01B6F3C485358_1601216530133_+2020-09-27+23.21.54.png)


きちんと取り込めましたね！
基本的に入力のチェックはしていないので入力値にミスがないことが前提です。
各自の環境によってカスタマイズしてみてください！


# 補足
## [1] 終日イベントかどうかの判断について
```javascript
 // [1]終日イベントかどうかの判定(開始時間の有無で判定)
 if (events[i][startTimeCol].length == 0)
```

終日イベントの判定はC行が空白の場合終日イベントに、そうでない場合は時間イベントで判断しています。


## [2]時間を跨ぐ終日イベントについて
```javascript
 // [2]イベント日数が日を跨ぐかどうかの判定。
 if (events[i][endDayCol].length == 0) {
```

時間を跨ぐ終日イベントの場合、終了日を1日進めないと正常に動作しません。
そのための判定を行い、必要に応じて日付を進めています。

```javascript

 // [2]終日イベントで、開始日と終了日が異なる場合、終了日を1日遅らせる必要がある。
 endTime.setDate(endTime.getDate() + 1);
```

## [3]日付と時間を合わせる操作について

```javascript

 // [3]日付と時刻を結合し、フォーマットする。
 startTime = new Date(Utilities.formatDate(events[i][startDayCol], "Asia/Tokyo", "yyyy/MM/dd") + " " +
 Utilities.formatDate(events[i][startTimeCol], "Asia/Tokyo", "HH:mm:ss"));
 endTime = new Date(Utilities.formatDate(events[i][endDayCol], "Asia/Tokyo", "yyyy/MM/dd") + " " +
 Utilities.formatDate(events[i][endTimeCol], "Asia/Tokyo", "HH:mm:ss"));
```

今回のスプレッドシートでは日付と時刻が別々のセルに別れて記されています。
そのため、日付のセルからは日付部分だけを、時刻のセルからは時刻だけを取得し、文字列として合わせて、その後Date型に変換する必要があります。
（もっと上手い方法があるかもしれません。ご存知の方がいらっしゃいましたらご教示いただけると幸いです。）
1つのセルに日付と時刻を記入すればこの様な手間のかかる処理はしなくてもいいのですが、今回は終日イベントと時間イベントを同時に組み込みたかったためこの様なコードとなりました。

## スプレッドシートで記入されている日付・時刻とGoogleカレンダーに登録された日付・時刻がずれる場合

GASのタイムゾーンがずれている可能性があります。
[こちら](https://qiita.com/park3taro/items/a4c61305386e151a6529)を参考にタイムゾーンを確認してみてください。