#はじめに
少し前に業務で入力日付の1ヶ月後を取得する処理を作ったが、予想以上に苦戦してしまったため記事にしました。特にJavaScriptの日付の初期化については、他の記事でもかなり見落としがあったため注意して欲しいです。
#概要
一か月後の定義は民法第143条第2項(暦による期間の計算)を参照する。
具体例を示すと
2020年1月13日の一ヶ月後→2020年2月12日
2020年1月31日の1ヶ月後→2020年2月29日
2020年1月1日の1ヶ月後→2020年1月31日
となる。
一ヶ月前はこの矢印を逆にすればよい(なお2月29日の一ヶ月前は1月30日である事には注意)。
画面は以下の記事を参考にして作成した。
UIのイケているdatepickerライブラリpickadate.jsが最高だった(demo有)
#主な実装方法
画面イメージは実際に作ったデモページを参考にして下さい。
デモページ
画面からは「yyyy年MM月dd日」の形式で入力日付を持ってくる事を想定する。
その場合はsubstringを使用して年、月、日を分割させる。
let year = date.substring(0,4);
let month = date.substring(5,7);
let day = date.substring(8,10);
次は1月31日を純粋に一ヶ月増やした、「2月30日」という存在しない日付を取得してこない処理を実装する(正確には、この処理を実装しない場合は2月30日ではなく、3月3日が返ってくる)。
あらかじめ、一ヶ月先の月(この場合は2月)の月末を取得し、『入力日付の「日」-1』と『月末の日付』を比較して、月末の日付を超えないように正しい日付を取得する。
月末は以下のようにして取得した。
let endOfMonth = (function (paraYear, paraMonth) {
let tempEndDate = new Date(paraYear, paraMonth, 0);
return tempEndDate.getDate();
})(year,Number(month) + Number(monthTerm));
入力された○ヶ月後(ヶ月前)の数字を、最初に取得した月の変数に加算する。
その後、年はsetFullYearを、月はsetMonthを、日はsetDateを使って算出する日付を作成する。
JavaScriptでは、Monthは0から始まる事に注意。
setMonth(0) // 1月を取得
setMonth(11) // 12月を取得
#月末の処理に注意
ここで一つ問題です。
let date = new Date();
date.setFullYear(2020);
date.setMonth(3);
date.setDate(1);
console.log(date);
このコードはいつの日付を返すでしょうか。
ここで2020年4月1日と直ぐに答えた人は要注意です。
正解は本日の日付が1日〜30日の場合は2020年4月1日で、31日の場合は2020年5月1日となります。
##どうしてか
JavaScriptにてnew Date()をした際は、現在日付が取得されます。
そのため、1月10日に上のコードを実行すると
let date = new Date(); // dateは2020/1/10
date.setFullYear(2020); // dateは2020/1/10
date.setMonth(3); // dateは2020/4/10
date.setDate(1); // dateは2020/4/1
console.log(date); // 2020/4/1を返す
となりますが、1月31日に実行すると
let date = new Date(); // dateは2020/1/31
date.setFullYear(2020); // dateは2020/1/31
date.setMonth(3); // dateは2020/4/31→4/31は存在しないため、繰り上がって5/1となる
date.setDate(1); // dateは2020/5/1
console.log(date); // 2020/5/1を返す
となります。
知っておくと問題のですが、知らない場合は嵌ってしまうので注意が必要です。
#コード
以下が○ヶ月後、○ヶ月前を取得するコードになります。
ライブラリーは各自で取得するか、下のGithubのコードを取得してください。
Github
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>日付計算</title>
<link rel="stylesheet" href="css/default.css">
<link rel="stylesheet" href="css/default.date.css">
<script src="Scripts/jquery-3.4.1.min.js"></script>
<script src="Scripts/picker.js"></script>
<script src="Scripts/picker.date.js"></script>
<script src="Scripts/main.js"></script>
<script src="Scripts/lang-ja.js"></script> <!-- 日本語化 -->
</head>
<body>
<section class="section">
<div class="section__block section__block--scoped">
<h3>日付計算</h3>
<fieldset class="fieldset fieldset--demo">
<div class="fieldset__wrapper">
<label>日付</label></br>
<input id='inputDate' class="fieldset__input js__datepicker" type="text" placeholder="Try me…"></br></br>
<label>求めたい未来(過去)</label></br>
<input type="number" id="inputMonth" min="0"/>
<form name="form1">
<input name="manth" id="next" type="radio" checked> ヶ月後
<input name="manth" id="pre" type="radio"> ヶ月前
</form>
<button id="submit" class="fieldset__button button button--small" onclick="onClickSubmit();">算出する</button>
</div>
<div id="result"></div> <!-- ボタン押下の結果を表示する -->
</fieldset>
</div>
</section>
</body>
</html>
$(function() {
//datepicker表示イベント
$('#inputDate').pickadate();
});
//算出ボタン押下処理
function onClickSubmit(){
let date = $('#inputDate').val();
// 日付が入力されていない場合は終了
if(date==''){
return;
}
// 結果出力部分を空白に
$('#submit_result').remove();
// 年、月、日をそれぞれ算出
let year = date.substring(0,4);
let month = date.substring(5,7);
let day = date.substring(8,10);
let checkNext = document.form1.next.checked; // ヶ月後の方にチェックが入っているか
let monthTerm = 0 // 入力したMonthの期間
// ヶ月後を選択の際はその値を、ヶ月前を選択の場合は-1をかけたものを月の期間にセットする
if(checkNext){
monthTerm = $('#inputMonth').val();
} else {
monthTerm = $('#inputMonth').val() * -1;
}
// 算出する日付の月末の日にちを取得
let endOfMonth = (function (paraYear, paraMonth) {
let tempEndDate = new Date(paraYear, paraMonth, 0);
return tempEndDate.getDate();
})(year,Number(month) + Number(monthTerm));
if (day > endOfMonth) {
day = endOfMonth;
} else {
if (checkNext) {
day = day - 1;
} else {
day = Number(day) + 1;
}
}
// 日付の初期化
let initDate = function () {
let date = new Date();
date.setDate(1);
return date;
};
let newDate = initDate();
newDate.setFullYear(year);
newDate.setMonth(Number(month) + Number(monthTerm) - 1);
newDate.setDate(day);
let reultYear = newDate.getFullYear();
let reultMonth = ('00' + (newDate.getMonth() + 1)).slice(-2);
let reultDay = ('00' + newDate.getDate()).slice(-2);
let result = reultYear + '年' + reultMonth + '月' + reultDay + '日';
//メッセージ表示
$('#result').after('<div id="submit_result" class="section__block section__block--notification"><p><strong>'
+ result
+ '</strong></p></div>');
}
#おわりに
意外にもネット上には、この形式の1ヶ月後を取得するサンプルコードがなかったため、自分で作成しました(あくまでも当時の話で、現在はあるのかもしれません)。
間違い、疑問点等がありましたらコメントにてお願いします。
#参考資料
[月末近くの日付でsetMonth()を使うとバグる? - tukihatuの技術日記]
(https://tukihatu.hatenablog.com/entry/20120531/1338458469)