GoogleAppsScript(GAS)でsetMinutesしたらハマった

GoogleAppsScriptでDo Loop中にsetMinutes()してたら構文的には合ってるのに
全くプログラムが動かなくなって白目剥いたので、メモ。

やっぱりGASはAPI呼び出しをループ内でするとしっかりダメになるなぁ。。。
#作成していたプログラムの概要
以下「情報」シートから『名前』と『時間区分』を取得して
![FireShot Capture 139 - 無題のスプレッドシート - Google スプレッドシート - docs.google.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90415/33bac7ae-3658-20ba-cb22-365dd77d7157.png)

別のシート「時間マスタ」と突き合わせを行い
![FireShot Capture 131 - 無題のスプレッドシート - Google スプレッドシート - docs.google.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90415/29e04902-e049-a742-05b7-13a57eca639c.png)

時間区分に合った形で、以下の様な形式でCSVファイルを出力する
![FireShot Capture 136 - 無題のスプレッドシート - Google スプレッドシート - docs.google.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90415/bf349f80-d457-8f1c-5e5c-bb4e70b5ebd0.png)
※鈴木の場合、時間区分はBなので10時～18時まで『XX:XX:XX.000Z』形式のレコードを30分刻みで終了時間まで作成する。


全体的なイメージは以下
![asdsad.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90415/b5ff0f4d-149d-8a12-fd31-fdf9dba2c1b8.png)

#時間マスタのデータを取得
細かい説明はコメントにて。
まずは動く様にしたコードを記載する。

```JavaScript:getTimeMaster.gs
function getTimeMaster() {
  //時間マスタのデータを二次元配列で一括取得
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var original = spreadsheet.getSheetByName('時間マスタ');
  //時間マスタの全データ取得
  var var1 = original.getDataRange().getValues();
  
  var timeMasterArray = [];
  //ヘッダー情報のデータは不要なので落とす
  for(var i=1; i < var1.length; i++){
    //開始時間、終了時間の型を取得
    var startTimeObjectType = Object.prototype.toString.call(var1[i][1]);
    var endTimeObjectType = Object.prototype.toString.call(var1[i][2]);
    
    //取得した型がDate型の場合のみ「HH:mm」形式に変換を行う
    if (startTimeObjectType == '[object Date]' && endTimeObjectType == '[object Date]') {
      var startTime = Utilities.formatDate(var1[i][1], "Asia/Tokyo", "HH:mm");
      var endTime = Utilities.formatDate(var1[i][2], "Asia/Tokyo", "HH:mm");
    } else {
      var startTime = var1[i][1];
      var endTime = var1[i][2];
    }
    //配列の後方に挿入
    timeMasterArray.push([var1[i][0], startTime, endTime]);
  }
  
  //時間管理マスタの内容を二次元配列で返す
  return timeMasterArray;
}
```
以下の様な配列ができる。
![asdadasdf.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90415/003d0bef-d912-5cd5-17ce-c0e89fbfe67c.png)

#メイン処理
```JavaScript:main.gs
function main() {
  //情報シートのデータを二次元配列で一括取得
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var original = spreadsheet.getSheetByName('情報');

  //メイン情報
  var mainDataArray = original.getDataRange().getValues();
  //時間マスタのデータを取得
  var timeMasterArray = getTimeMaster();

  //CSV用の配列を用意
  var workScheduleCSV = [
    ["name", "type", "time"]
  ];

  //取得情報のマッチングループ処理
  //ヘッダ情報がある為、0ではなく1から開始
  for(var i=1; i < mainDataArray.length; i++){
    //エラーカウンタを初期化
    var timeCnt = 999999;

    //時間マスタのマッチング処理
    for(var j=0; j < timeMasterArray.length; j++){
      var result = timeMasterArray[j].indexOf(mainDataArray[i][1]);
      //マッチングした場合結果を格納
      if(result !== -1){
        timeCnt = j;
        break;
      }
    }
    //時間マスタと一致しなかった場合エラーを返す
    if(timeCnt == 999999) {
        throw new Error("時間区分が時間マスタと一致しません。作成したシートを見直してください。");
    }
    //開始時間を[XX, XX]で取得
    var timerStart = timeMasterArray[timeCnt][1].split(':');
    //終了時間を[XX, XX]で取得
    var timerEnd = timeMasterArray[timeCnt][2].split(':');

    //スケジュールを初期化
    var timerSchedule = [];
    //開始時間を設定して初期化
    var setTimerArray = timerStart;
    do {
      //「XX:XX:000.000Z」形式に変換してスケジュール配列に設定
      timerSchedule.push(setTimerArray[0] + ":" + setTimerArray[1] + ":00.000Z");

      //時間に30分を追加
      setTimerArray[1] = Number(setTimerArray[1]) + 30;
      //既に30分追加されていた場合、分を00に戻して時間に1を足す
      if (setTimerArray[1] == 60) {
        setTimerArray[1] = "00";
        setTimerArray[0] = Number(setTimerArray[0]) + 1;
      }
    //終了時刻を超えるまでループ
    } while (Number(setTimerArray[0] + "" + setTimerArray[1]) < Number(timerEnd[0] + "" + timerEnd[1]) + 1);
    for(var z = 0; z < timerSchedule.length; z++) {
      //csv用配列にレコードを追加
      workScheduleCSV.push([mainDataArray[i][0], mainDataArray[i][1], timerSchedule[z]]);
    }
  }
  //現在日付を取得
  var date = new Date();
  var date_D = new Date(date.getFullYear(),date.getMonth(),date.getDate(),0,0,0);
  //yyyyMMdd形式に変換
  date_D = Utilities.formatDate(date_D, "Asia/Tokyo", "yyyyMMdd");
  //CSV用の配列を出力
  createCSV(workScheduleCSV, date_D);
}
```


#CSV出力処理
```JavaScript:createCSV.gs
function createCSV(Array, fileName) {  
  //受け取った二次元配列をカンマ区切りの文字列に変換
  var csv = Array.join('\n');
  
  //Blobオブジェクトの作成
  var blob = Utilities.newBlob(csv, MimeType.CSV, fileName + ".csv");
  
  //CSVファイルの保存先フォルダを指定しない場合(スプレッドシートファイルと同一フォルダに出力)
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var folder = getParentFolder(ss);
  
  //CSVファイルの保存先フォルダを指定する場合
  //フォルダID
//  var id = "#######ここにIDを記載#######";
//  var folder = DriveApp.getFolderById(id);
  
  //CSVファイルを作成
  folder.createFile(blob);
}

function getParentFolder(ss) {
 //スプレッドシートのIDを取得
  var ssId = ss.getId();  
  var parentFolders = DriveApp.getFileById(ssId).getParents();
  var parentFolder = parentFolders.next();
  
  return parentFolder;
}
```
#出力結果CSV中身
![csv中身.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90415/5f60af7f-c54f-c74c-c9ce-e6ebb1883fd7.png)


#ハマったところ
```JavaScript:main.gs
    do {
      //「XX:XX:000.000Z」形式に変換してスケジュール配列に設定
      timerSchedule.push(setTimerArray[0] + ":" + setTimerArray[1] + ":00.000Z");

      //時間に30分を追加
      setTimerArray[1] = Number(setTimerArray[1]) + 30;
      //既に30分追加されていた場合、分を00に戻して時間に1を足す
      if (setTimerArray[1] == 60) {
        setTimerArray[1] = "00";
        setTimerArray[0] = Number(setTimerArray[0]) + 1;
      }
    //終了時刻を超えるまでループ
    } while (Number(setTimerArray[0] + "" + setTimerArray[1]) < Number(timerEnd[0] + "" + timerEnd[1]) + 1);
    for(var z = 0; z < timerSchedule.length; z++) {
      //csv用配列にレコードを追加
      workScheduleCSV.push([mainDataArray[i][0], mainDataArray[i][1], timerSchedule[z]]);
    }
  }
```
メイン処理のこのDo Loop内で、時間をDateで管理して
1ループごとに『へんすう.setMinutes(へんすう.getMinutes() + 30);』
してたらタイムアウトして全く動かなくなったのであった。

#結論
GASは定期的に「え！？これ理論的には合ってるのに止まってんじゃねーよ」が発生すること多すぎてきつい
もうGASでコード書きたくない。。。ゆるして。。。