LoginSignup
5
2

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-08-30

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

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

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

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


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

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

MHMchiX6c1bwSqGM1PZiW_PxhMjh3Sh48

やりたかったこと

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

task.png

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

ダメだった例

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

始めはこう書きました。

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」 なのです。

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

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日。

という動きのようだ。

どうすればいいか

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

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

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」が出力されます。

↓ 念のため実験

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ソースコードを見てみました。(これはmoment.jsのソースコードであってGASのライブラリのコードではないので、GASでは違う実装かもしれませんが、きっと同じであろう)

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 は指定していないので、

return asFloat ? output : absFloor(output);

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

absFloor() 関数の定義は こちら

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)はゼロで埋めてあげる

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

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

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

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2