見出し画像

[GAS]日付をincludesしたら意図した判定にならなくて困った。日付はいつもこれだよ。typeofしたり、そもそもクラスの理解が甘いし、つうかカレンダでどうにかするとかいろいろあって分からんくなってきた。とにかくまずはこのincludesの判定を意図したものになるようどうにかするぞ。そんでクラスの理解がやっぱ必要だよっていう自分のためのメモ。

タイトルが長すぎる。

日付は曲者なんだよな~。
ということで、ちょっとハマっていたポイントのメモ。

見た目一緒でも別々のインスタンスだということをちゃんと理解してなかったな。

やっぱクラスだ、クラスの理解があああああああ。

現象

こんなコードがあるとして、これを実行するとこうなんですよ。

new Date('2022/12/29')なら、
期待した結果は
console.log(winterHolidays.includes(targetDate)); がtrueなのにそうならんのです。

コード

https://dev.classmethod.jp/articles/202001-workday-only-gas/  を参考に、冬期休暇も追加したいぞ、ってことで書いてみたんですね。

でも、そもそも、Googleカレンダの日本の祝日+独自休暇を足した会社休日用カレンダを作ってそこから判定させるのもありだよな..。あ~実行者がカレンダ読み込んでないと?スプレッドシートにばーっと書いといて、それを持ってくるのもありか?

ちょっとそれらはおいといて、まずは、とにかくこの判定問題にフォーカスして考えたい。

/**

 * 参考
 * https://dev.classmethod.jp/articles/202001-workday-only-gas/
 */


// 指定された日が営業日か(営業日 = 「土日でない」「祝日カレンダーに予定がない」)
// 営業日 = true
function isWorkday(targetDate = new Date('2022/12/29')) {

  console.log({ targetDate });

  // targetDate の曜日を確認、週末は休む (false)
  const rest_or_work = ["REST", "mon", "tue", "wed", "thu", "fri", "REST"]; // 日〜土
  if (rest_or_work[targetDate.getDay()] == "REST") {
    console.log(`今日は 土日ですよ`);
    return false;
  };

  // 祝日カレンダーを確認する
  const calJpHolidayUrl = "ja.japanese#holiday@group.v.calendar.google.com";
  const calJpHoliday = CalendarApp.getCalendarById(calJpHolidayUrl);
  if (calJpHoliday.getEventsForDay(targetDate).length != 0) {
    // その日に予定がなにか入っている = 祝祭日 = 営業日じゃない (false)
    console.log(`今日は 祝日ですよ`);
    return false;
  };

  //会社指定の休日(上記の祝日カレンダには入っていない休日、休暇)スプレッドシートから取得
  const winterHoliday1 = dateMasterSheet.getRange('D31').getValue();   //12/29
  const winterHoliday2 = dateMasterSheet.getRange('D32').getValue();   //12/30
  const winterHoliday3 = dateMasterSheet.getRange('D33').getValue();   //12/31

  console.log({ winterHoliday1 });

  const winterHolidays = [winterHoliday1, winterHoliday2, winterHoliday3];

   console.log({ winterHolidays });


  //winterHolidays に targetDate が含まれていたら true
  console.log(winterHolidays.includes(targetDate));

  if (winterHolidays.includes(targetDate) == true) {
    //targetDate が冬季休暇日だったらfalse
    console.log(`今日は 冬季休日ですよ`);
    return false;
  }

  // 全て当てはまらなければ営業日 (True)
  console.log(`今日は 営業日ですよ`);
  return true;
}

実行ログ

えー、12/29は冬季休日として判定して欲しいんですよお。

スプレッドシート

getRange('D31')  とかでスプレッドシートから取ってきてる値の元はこんな感じ。

=DATE(YEAR(TODAY()),12,29)

原因

それぞれの日付は見た目は一緒でも別々のインスタンスだから。
まったく同じ見た目でも、別のものなんだよ~~~。

でも、これ、まだちょっとよく理解できてない。
特に、まずそもそものクラスからですよ、理解が甘いのは。

見た目は一緒でも別々のインスタンス、の例としてのコード。

function cat() {
  class Cat {
    constructor(value) {
      this.value = value;
    }
  }

  const test1 = new Cat('ねこ');
  const test2 = new Cat('ねこ');

  console.log({ test1 });
  console.log({ test2 });

  console.log(typeof test1);//object
  console.log(typeof test2);//object


  console.log(`等値演算子 ==  : ${test1 == test2}`); //false
  console.log(`同値演算子 === : ${test1 === test2}`); //false

  console.log(`等値演算子 ==  : ${test1.value == test2.value}`); //false
  console.log(`同値演算子 === : ${test1.value === test2.value}`); //false
}

constructor とか、そのへん、やっぱ腑に落ちてないわ。
なんでこれで新しいオブジェクトが作れてるのかがわかんない。
そういうものだというのはわかる、でもなんかこう、わかんない。
わからんけどこう書くとできる、というのだけわかる。
わからん。



検証

typeofしてみる

両方オブジェクトじゃん~~~~。


文字列で比較

スプレッドシート上の表記をyyyy/MM/dd にして、それをgetDisplayValueする。
targetDateもUtilities.formatDateでyyyy/MM/ddにして比較すると、期待した結果が得られる。

コード

// 指定された日が営業日か(営業日 = 「土日でない」「祝日カレンダーに予定がない」)
// 営業日 = true
function isWorkday(targetDate = new Date('2022/12/29')) {//テスト用 new Date('2022/12/30')

  console.log({ targetDate });
  console.log(typeof targetDate);

  // 省略

  //会社指定の休日(上記の祝日カレンダには入っていない休日、休暇)スプレッドシートから取得
  const winterHoliday1 = dateMasterSheet.getRange('D31').getDisplayValue();  //2022/12/29
  const winterHoliday2 = dateMasterSheet.getRange('D32').getDisplayValue();  //2022/12/30
  const winterHoliday3 = dateMasterSheet.getRange('D33').getDisplayValue();  //2022/12/31

  console.log({ winterHoliday1 });
  console.log(typeof winterHoliday1);

  const winterHolidays = [winterHoliday1, winterHoliday2, winterHoliday3];
  console.log({ winterHolidays });

  const str_targetDate = Utilities.formatDate(targetDate, 'JST', 'yyyy/MM/dd');//比較用に2022/12/29 の形式にする
  console.log({ str_targetDate });
  console.log(typeof str_targetDate);


  //winterHolidays に targetDate が含まれていたら true
  console.log(winterHolidays.includes(str_targetDate));

  if (winterHolidays.includes(str_targetDate) == true) {
    //targetDate が冬季休暇日だったらfalse
    console.log(`今日は 冬季休日ですよ`);
    return false;
  }

  // 全て当てはまらなければ営業日 (True)
  console.log(`今日は 営業日ですよ`);
  return true;
 }

実行ログ
ちゃんと両方stringになってる。


getTime()で比較

function isWorkday(targetDate = new Date('2022/12/29')) {//テスト用 new Date('2022/12/30')

  console.log({ targetDate });
  console.log(typeof targetDate);

  const getTimeOfTtargetDate =  targetDate.getTime();
  console.log({ getTimeOfTtargetDate });
  console.log(typeof getTimeOfTtargetDate);


  //省略

  //会社指定の休日(上記の祝日カレンダには入っていない休日、休暇)スプレッドシートから取得

  const winterHoliday1 = dateMasterSheet.getRange('D31').getValue().getTime();  
  const winterHoliday2 = dateMasterSheet.getRange('D32').getValue().getTime();  
  const winterHoliday3 = dateMasterSheet.getRange('D33').getValue().getTime();  
  // const winterHoliday1 = dateMasterSheet.getRange('D31').getDisplayValue();  //2022/12/29
  // const winterHoliday2 = dateMasterSheet.getRange('D32').getDisplayValue();  //2022/12/30
  // const winterHoliday3 = dateMasterSheet.getRange('D33').getDisplayValue();  //2022/12/31

  console.log({ winterHoliday1 });
  console.log(typeof winterHoliday1);

  const winterHolidays = [winterHoliday1, winterHoliday2, winterHoliday3];
  console.log({ winterHolidays });

  // const str_targetDate = Utilities.formatDate(targetDate, 'JST', 'yyyy/MM/dd');//比較用に2022/12/29 の形式にする
  // console.log({ str_targetDate });
  // console.log(typeof str_targetDate);


  //winterHolidays に targetDate が含まれていたら true
  console.log(winterHolidays.includes(targetDate.getTime()));

  if (winterHolidays.includes(targetDate.getTime()) == true) {
    //targetDate が冬季休暇日だったらfalse
    console.log(`今日は 冬季休日ですよ`);
    return false;
  }

  // 全て当てはまらなければ営業日 (True)
  console.log(`今日は 営業日ですよ`);
  return true;
}

実行ログ



クラスについて、先人の語録

日付はクラスです。(だからnew しますよね)new すると、インスタンスが生成されます。

一方、スプレッドシートから日付データをgetValueすると、自動的に、
日付インスタンスがnew されます。すべて値は一緒でも、「別々のインスタンス」です。
なので、includeで比較すると、たまたま値が一緒の別のインスタンスなので、falseを返すんです。

文字列で比較するか、日付オブジェクト.getTime()してから比較すると、同じ日付であれば、trueが帰ってきます。
※どちらにしてもformatDateか、setHours(0,0,0,0)せずに、そのまま比較にすると、ミリ秒単位まで比較しちゃうので注意です!

かとう語録


「定数」に格納してるのに、配列の要素の書き換えができちゃう問題に対する回答

配列を入れた定数には、配列の「住所」が入っています。 配列を、お家だと考えてみてください。 お家には、小部屋がいっぱいあります。 定数なので、「配列の住所」は、変更不可です。 でも、各部屋の中身は、模様替えが出来るんです。

こまつ語録

インスタンスの場合、インスタンスをお家と考えると、
newするごとに、新しいお家が建設されます。
いくら内装、間取りをすべて一緒にしても、同じ住所には、何件も家は建てられないですよね。
なので、newするごとに、各インスタンスは別の「住所」を持ちます。
それを比較演算子で比較すると、住所が違うので、falseになるんです。

かとう語録


言ってることは、わかる、わかっている、と思う。でも自分で説明できない、うーん、やっぱまだどこか、引っかかってるなこれ。
インスタンスは内部的にそれぞれユニークなIDを持ってる的な。
わかってないことは分かるが、なにがわからないのかがわからん。
クラスをちょっと復習、クラスのサンプルコードやらなんやら書いて理解する必要があるぞこれは。

カレンダがどうこう、営業日通知したいのがあるが、それはそれとしてクラスの理解が必要だ。


ノンプロ研のみなさん、いつも本当にありがとう!

#ノンプロ研
#GAS
#ハマったポイント


いただいたサポートで、書籍代や勉強費用にしたり、美味しいもの食べたりします!