[GAS][JS] Momentライブラリのdiffでの日付差分

# (追記 2020/09/19) Moment.js の開発が終了したようです。

↓ ここ参照
https://momentjs.com/docs/

今すぐライブラリが使えなくなることは無いと思いますが、この先、積極的にこのライブラリを選択するかどうかは考えものですね。

あと、GAS版のライブラリがどうなるのか、については情報が見つかりませんでした。(どなたか知ってたら教えてほしい)

---- 

GASで話をしますが、Javascriptでも同じです。

GAS版 MomentライブラリのIDは下記です。

```
MHMchiX6c1bwSqGM1PZiW_PxhMjh3Sh48
```

# やりたかったこと

こんな表を作って、毎朝8時台にGASが起動して、「締め切りまであと3日」以内になったら「締め切り近いけど大丈夫？」というリマインドをしてくれる機能を作りたい。

![task.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8612/a6948f11-d9f2-ecd9-337c-35eb5915476d.png)

そのため、**Momentライブラリをつかって「日付の差分」を取りたかった。**

# ダメだった例

簡略化するためにA2セルの日付だけを対象にします。

始めはこう書きました。

```js
function sample1() {
  // 日付のセルからgetValue()したら Dateオブジェクト が取れるのでこう書いてしまいます
  const deadline_date = new Date("2020-09-10");

  const deadline_moment = Moment.moment(deadline_date);
  const now_moment      = Moment.moment();

  const diff = deadline_moment.diff(now_moment, "days");
  console.log(diff);
}
```

そして「今日は **2020/9/9** だとする」じゃないですか。
A2セルは **2020/9/10** なので 1 が出力されることを期待するじゃないですか。
だけど **出力結果は「0」**


# ダメだった理由

上記で 「今日は **2020/9/9** だとする」 と書いたのですが、
実際はこのGASが実行される時刻は **2020/09/09 12:34:56** だったりするわけです。

そして deadline として指定しているのは 「2020-09-10 00:00:00」 なのです。

実験してみるとこうなりました↓

```js
function sample2() {
  const deadline_date = new Date("2020-09-10 00:00:00");
  const deadline_moment = Moment.moment(deadline_date);
  
  const now_moment1 = Moment.moment("2020-09-09 00:00:00");
  const diff1 = deadline_moment.diff(now_moment1, "days");
  console.log(diff1); //=> 1 
  
  const now_moment2 = Moment.moment("2020-09-09 00:00:01");
  const diff2 = deadline_moment.diff(now_moment2, "days");
  console.log(diff2); //=> 0
}
```

now_moment1 と now_moment2では「1秒」異なります。

diff1 では deadlineである 2020-09-10 00:00:00 と **2020-09-09 00:00:00** の差分が **24:00:00** なので 1日。
diff2 では deadlineである 2020-09-10 00:00:00 と **2020-09-09 00:00:01** の差分が **23:59:59** なので 0日。

という動きのようだ。

# どうすればいいか

「日付」で比較したいのだからこうしたらいいのでは。

**「時分秒ミリ秒にゼロをセットしちゃう」作戦**

```js
function sample3() {
  const deadline_date = new Date("2020-09-10 00:00:00");
  const deadline_moment = Moment.moment(deadline_date);

  // 時分秒ミリ秒にゼロをセットしちゃう
  const today_moment = Moment.moment().hour(0).minutes(0).second(0).millisecond(0);
  const diff = deadline_moment.diff(today_moment, "days");
  
  console.log(diff);
}
```

これで、実行時刻が「2020-09-09 11:11:11」であっても「1」が出力されます。

↓ 念のため実験

```js
function sample4() {
  const deadline_date = new Date("2020-09-10 00:00:00");
  const deadline_moment = Moment.moment(deadline_date);
  
  const now = new Date("2020-09-09 11:11:11")
  const today_moment = Moment.moment(now).hour(0).minutes(0).second(0).millisecond(0);
  
  const diff = deadline_moment.diff(today_moment, "days");
  console.log(diff); //=> 1
}
```

予想通り！

# 本当にそうなの？

"days" で比較するときに、「24時間未満かどうかで判定している」というのは、動作させた結果を見て私が推測しているところなのですが、実際どういう実装になってるのか [momentライブラリのdiff.jsソースコード](https://github.com/moment/moment/blob/2c0b063b3bf95a285f9b38c173e262b6416c2e7f/src/lib/moment/diff.js#L41)を見てみました。(これはmoment.jsのソースコードであってGASのライブラリのコードではないので、GASでは違う実装かもしれませんが、きっと同じであろう)


```js
export function diff(input, units, asFloat) {
    var that, zoneDelta, output;

    if (!this.isValid()) {
        return NaN;
    }

    that = cloneWithOffset(input, this);

    if (!that.isValid()) {
        return NaN;
    }

    zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4;

    units = normalizeUnits(units);

    switch (units) {
// 略
        case 'day':
            output = (this - that - zoneDelta) / 864e5;
            break; // 1000 * 60 * 60 * 24, negate dst
// 略
    }

    return asFloat ? output : absFloor(output);
}
```

`864e5` とは 1日をミリ秒にしたときの 86400000 の指数表記。
よって **時刻の差分を 1日を表すミリ秒で割ってる** のね。


diff関数の第3引数である `asFloat` は指定していないので、

```js
return asFloat ? output : absFloor(output);
```

では `absFloor(output)` が return される。


`absFloor()` 関数の定義は [こちら](https://github.com/moment/moment/blob/2c0b063b3bf95a285f9b38c173e262b6416c2e7f/src/lib/utils/abs-floor.js#L1)

```js
export default function absFloor(number) {
    if (number < 0) {
        // -0 -> 0
        return Math.ceil(number) || 0;
    } else {
        return Math.floor(number);
    }
}
```


Math.ceil() : 引数として与えた数以上の最小の整数を返します。
Math.floor() : 引数として与えた数以下の最大の整数を返します。

ということなので、output(時刻の差分を 1日を表すミリ秒で割った数値) が 

* 0.999 であれば 0 を返すし、
* 1.234 なら 1 を返すし、
* -3.55 なら -3 を返す。

私の推測はあってたようです！(...というここまでの流れが間違ってたら教えてほしいです)

# 結論

ということで **「日にち」を比較したいのであれば、日にち(date)より小さい単位(hour, minutes, second, millisecond)はゼロで埋めてあげる**


補足ですが、ミリ秒がでてくるなら

```js
const deadline_date = new Date("2020-09-10 00:00:00.000");
```

としたほうが粒度は揃いますね。
(ミリ秒まで指定してDateオブジェクトをnewしたことが無いのでへんな感じする)