見出し画像

【JavaScript】Date.SetMinutes()で時間計算する(時に、ハマりまくった話)

結論

let now = new Date(); // 常に現在時刻を入れておく為のオブジェクト

function calc(){
    let now = new Date(); // 使い捨てにする計算用。必ず別に定義する。    
    let people = document.getElementById("people").value; // 入力フォームのvalueを取得
    let add_minutes = 60; // 足したい分数
    
    let end_date = new Date(now.setMinutes( now_date.getMinutes() + add_minutes ));
    let display_time = format_date(end_date);
    
    document.getElementById("result").innerHTML = display_time;
}

function format_date(date){
  let h = ("00" + now.getHours()).slice(-2);
  let m = ("00" + now.getMinutes()).slice(-2);
  return h + ":" + m;
}
  
function reload_now(){
  now = new Date();
}

// 入力フォームを取得
let input_element = document.getElementById("people")

// 入力フォームに入力があったらcalc()を発火させる
window.addEventListener("change", calc()) 

// 毎分nowを更新する
​setInterval(reload_now, 60 * 1000);

作りたかったもの

ある仕事をするのに、1人でやると60分掛かります。
(2人でやれば30分、3人でやれば20分、と単純化して計算します)
人数を入力する欄があるので、入力された人数に応じて、終了時刻を計算して表示して下さい。

できたもの

入力値を変える度にランダムに時刻が変化するもの

どうして???

原因1:Date.setMinutes()の使い方間違えてた

誤:

let end_date = new Date(now.setMinutes( add_minutes ));

正:

let end_date = new Date(now.setMinutes( now.getMinutes() + add_minutes ));

Date.setMinutes()は、本来、0~59までの整数値を与えて、そのDateオブジェクトの「分」の値を強制的にその分数に設定してしまうというメソッドなんですって。

ただし、59以上の値が与えられた場合、

指定した値が期待される日時の範囲外の場合、それに応じて setMinutes() が Date オブジェクトの日付情報の更新を試みます。例えば、secondsValue に 100 を指定した場合、分に 1 が加算 (minutesValue + 1) され、秒が 40 になります。

ってことで、「上手いこと計算しようと頑張ってくれる」そうです。

なので、「足したい分数」を与えるのではなく、「現在時刻の分の位 + 足したい分数」を計算した値を渡す必要があります。

これで良い感じに計算してくれます。

※なお、この「良い感じの計算」を月またぎの日付計算で使いたいときはちょっと注意が必要なんですが、今回関係ないので必要な人は調べておくれ。

原因2:上記の計算する度にnowが上書きされまくってる

誤:

let now = new Date();  // 現在時刻
// ※別メソッドで毎分取得し直している

function calc(){
    let people = document.getElementById("people").value; // 入力フォームのvalueを取得
    let add_minutes = 60; // 足したい分数
    
    let end_date = new Date(now.setMinutes( now.getMinutes() + add_minutes ));
    let display_time = format_date(end_date);
    
    document.getElementById("result").innerHTML = display_time;
}

正:

let now = new Date();

function calc(){
    let now_date = new Date(); // 使い捨てにする計算用Dateをfunction内で定義する    
    let people = document.getElementById("people").value; // 入力フォームのvalueを取得
    let add_minutes = 60; // 足したい分数
    
    let end_date = new Date(now_date.setMinutes( now.getMinutes() + add_minutes ));
    let display_time = format_date(end_date);
    
    document.getElementById("result").innerHTML = display_time;
}

誤の方では、

    let end_date = new Date(now.setMinutes( now.getMinutes() + add_minutes ));

この処理をするたびに、SetMinutes()メソッドにより「now」には新しい時刻(仕事に必要な時間を足した後の時刻)がセットされてしまいます。

(nowの現在時刻を取得して、それに必要時間を足した結果を、nowにセットし直す、という処理をしている。newで作り直しているような気がするけど、nowのsetMinutes()を呼んだ時点でnowに新しい時間がセットされちゃう

そのうえ、別メソッドでやってるnowの更新が1分ごとなので、1分以内に何度も入力値を変えると、どんどん時間が足されていく、と。そのため、どんどんどんどん時間が先送りされて、一見ランダムに時間が変わっているように見えていたのでした。表示時刻が進んだり戻ったりしているように見えたのは、数日先までタイムワープしていたからだったという…………

対処方法としては、本来nowを使い回さず毎回作り直すのが正解なのですが、今回は別メソッドでも同じnowを使いたい事情があったので、わざわざfunction外で定義しています。

なので、「後から必要な時間を足す為の暫定now_date」をfunction内で新たに定義して、大元のnowが上書きされることを防ぐことにしました。

これで正しく計算されるようになりました。やったね。

この記事が気に入ったらサポートをしてみませんか?