moment.jsで単位を日に指定してdiffを使う時、時間を考えず日が変わったかどうかだけで比較する

結論: startOf('days')で時間をまるめる

const dayOne = moment('2020-01-01 10:00:00').startOf('days');
const dayTwo = moment('2020-01-02 09:00:00').startOf('days');
console.log('dayOne', dayOne.format());
console.log('dayTwo', dayTwo.format());
console.log('diff', dayTwo.diff(dayOne, 'days'));

スクリーンショット 2020-03-22 22.51.26.png

はじめに

こんにちは、フロントエンドチームの高田(@proghallelujah)です。

moment.jsには指定した二つの日時を比較し、出力するdiffという機能があります。

この機能は、2つの日付の差分を時間単位まで観測し、デフォルトでは秒数で出力されます。 diffは第二引数で測定する時間単位を指定することが可能です。しかしその時、内部では秒数を任意の単位に再計算した値を返しています。 また、その時小数点以下は切り捨てられてしまいます

const dayOne = moment('2020-01-01 10:00:00');
const dayTwo = moment('2020-01-02 11:00:00');
const dayThree = moment('2020-01-02 09:00:00');
const dayFour = moment('2020-01-02 10:00:00');

console.log("dayTwo", dayTwo.diff(dayOne, 'days')); // dayTwoはdayOneの時間を過ぎているので1
console.log("dayTwo asFloat", dayTwo.diff(dayOne, 'days', true));
console.log("dayThree", dayThree.diff(dayOne, 'days')); // dayThreeはdayOneの時間を過ぎてないので0
console.log("dayThree asFloat", dayThree.diff(dayOne, 'days', true));
console.log("dayFour", dayFour.diff(dayOne, 'days')); // dayFourはdayOneと同じ時間なので1
console.log("dayFour asFloat", dayFour.diff(dayOne, 'days', true));

スクリーンショット 2020-03-22 22.49.55.png

(tips: 第三引数にtrueを渡すことで値から小数点を切り捨てず返されます)

以下はmomentjsでdiffを行なっている箇所です。 GitHub - moment.js/moment/src/lib/moment/diff.js


厳密な日付を計算するのであればこれが最良ですが、時間を考えず単純に日にちが変わったかどうかを計算したい、といった場合も多いと思います。 startOfを使えば、そのような機能を簡単に実装することができます。

startOfとは

momentの機能の一つで、指定した時間単位の始まりの値を出力します。 スクリーンショット 2020-03-22 23.02.30.png startOfでdayを指定すると、日のはじまり0:00:00に時刻を変換して出力されます。

参考: moment.js - Start of Time

ではstartOfを用いでdiffを使ってみます。

const dayOne = moment('2020-01-01 10:00:00').startOf('days');
const dayTwo = moment('2020-01-02 09:00:00').startOf('days');
console.log('dayOne', dayOne.format());
console.log('dayTwo', dayTwo.format());
console.log('diff', dayTwo.diff(dayOne, 'days'));

スクリーンショット 2020-03-22 22.51.26.png

diffは単位以下の値が同値の場合は1になるので、これで日にちが変わったかどうか計算することができます。

アンチパターンMath.roundを使う

小数点以下が切り捨てられるなら、Math.roundで繰り上げるようにすればよい、という案もあります。 しかし、Math.roundは意図せぬ値まで繰り上げてしまいます。

1. 第三引数にtrueを渡し、Math.roundを使う

const dayOne = moment('2020-01-01 01:00:00');
const dayTw0 = moment('2020-01-02 00:00:00');
const dayThree = moment('2020-01-01 17:00:00');
console.log(Math.round(dayTwo.diff(dayOne, 'days', true)), dayTwo.diff(dayOne, 'days', true));
console.log(Math.round(dayThree.diff(dayOne, 'days', true)), dayThree.diff(dayOne, 'days', true));

スクリーンショット 2020-03-22 23.10.12.png この場合、ある程度時間がたってしまった値だと、日付が同じでも繰り上げてしまいますので使用することができません。 なので、startOfが現状最良の手段だと思われます。


いかがでしたでしょうか?時間単位のはあくまで例に使っただけなので、その他の単位でも使用することができると思います。momentはドキュメントが丁寧でわかりやすく、自動翻訳でもそれなりに見ることができるので、皆さんもよかったらご一読ください。

moment.js - Docs