概要
「getMonth()」を使用して、日付の比較を行うプログラムを実装しましたが
実行時特有のJavaScriptの+1の概念で思わぬ事件を生み出してしまたため、記事にします。
やりたかったこと
JavaScriptを使用してユーザーが入力した日付のチェックを行いたかった。
例えば過去日を入力した場合には過去日を入力しないようにダイアログをだしたかった。
実装内容(修正前)
当初は以下のような実装を行っていた。
一見すると問題ないように見えるが、2023年1月29日に事件は発生した!!
// 現在の日時を取得
var now = new Date();
var nowDate = new Date(now.getFullYear(), now.getMonth() +1, now.getDate(), '00', '00', '00');
// ユーザーが入力した日時を取得
var targetDate = $(this).val().split('/');
var checkDate = new Date (targetDate[0], targetDate[1], targetDate[2], '00', '00', '00')
getMonth()のおさらい
そもそも「getMonth()」とは。。。。
月を0から11の整数で取得します。 基準となるのは、そのタイムゾーンの現地時間(ある地点の子午線を基準として>定めた地方時)です。
JavaScriptのgetMonth()メソッドでは、指定日の「月」から 1 を引いた数値が返ることに注意してください。 >getFullYear()メソッドやgetDate()メソッドでは、指定日の「西暦年」や「日」がそのまま返りますが、>getMonth()メソッドだけは「月 - 1」が返ります。
上記から当初は、現在日時を取得する場合には、+1をすればよいと考えていた。
しかし、上記の実装で、比較を行ったところ、月末に悲劇を迎えることになる。。。
何が起こったか
2023年1月30日、ユーザーは「2023年2月1日」を入力すると、未来を入力したはずが、
過去日と判定されてしまった。
その時、比較していた変数は以下の状態を返していたのである。。。
// 現在の日時を取得
var now = new Date();
// now ⇒ 2023/1/29
var nowDate = new Date(now.getFullYear(), now.getMonth() +1, now.getDate(), '00', '00', '00');
// now.getFullYear() ⇒ 2023
// now.getMonth() +1 ⇒ 1
// now.getDate() ⇒ 29
// nowDate ⇒ 2023/3/1 ※!?!?!?!?!?
ここで、Date()についてもおさらいを行いたい。
Date()のおさらい
様々な使い方がありますが、今回の今回の使用例としては、
日付の各要素を指定してDateを生成し、Dateで比較を行った。
new Date(year, monthIndex, day, hours, minutes, seconds)
注意点として、、、
いずれかの引数が定義された境界を超えた場合、「繰り上げ」が行われます。例えば、 monthIndex に 11 よりも>大きな値が渡された場合、その月は年を増加させます。 minutes に 59 よりも大きな値が渡された場合、hours はそれに応じて増加します。したがって、 new Date(1990, 12, 1) は 1991 年 1 月 1 日を返し、 new Date(2020, 5, 19, 25, 65) は 2020 年 6 月 20 日の午前 2 時 5 分を返します。
monthIndex
月を表す整数値です。 0 (1 月)から 11 (12 月)までの値です。
こちらも同じく、月は、0から11を指定する
ほぉ。。。。
つまり引数に、「2023,1,28 」を渡すと、「2023/2/28 00:00:00」を返すことになる。
じゃあ、「2023/1/29」、「2023/1/30」、「2023/1/31」はどうなるかというと。。
「2023/3/1」、「2023/3/2」、「2023/3/3」に繰り上がったということか!!!
修正したコード
// 現在の日時を取得
var now = new Date();
var nowDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), '00', '00', '00');
// ユーザーが入力した日時を取得
var targetDate = $(this).val().split('/');
// 比較対象の月をマイナスした
var checkDate = new Date (targetDate[0], targetDate[1]-1, targetDate[2], '00', '00', '00')
getMonth()がマイナス1で返すものの、Date()により、該当月を返す形になるため、
比較対象の月をあえてマイナスし、Dateの繰り上がり等を発生させてないように修正した。
そもそも、dateで比較を実施すること自体がナンセンスなきもするが、、
比較対象のデバックを行うとともに、JavaScriptでのgetMonthやdate()の使い方には気を付けたい。