「○○ちゃんが生まれてから、今日で何日目だか、数えていたんだけど分からなくなっちゃった」と、生後何百日目だかの幼児のママから相談がありました。
そういうプログラムを一般のプログラミング言語作るのは、さほど難しくないし、プログラムせずともExcelで計算することもできてしまう。
とはいえ、幼児のママが、たかが日数計算如きでそんなにホイホイとパソコンを立ち上げてプログラムを起動するわけにもいかないし、Excelが使えるわけでもない。
ただ、ふだんから片手で携帯電話(ガラケー)をいじって、Webサイトを見たりメールを打ったりすることはできるので、今回のソリューションもその範囲でどうにかするのが良いんじゃないかな、と。
考えてみると、今は凄い時代なので、ガラケーだろうとNintendo3DSだろうと、そこそこのブラウザが入っていて、JavaScriptが実装されていて、だいたい思い通りのプログラムを動かせたりする。
Nintendo3DSのソフトや、ガラケー用のアプリや、iOS/Androidアプリを本気で作ろうとすると、結構大変だし、法人格の取得とか、審査や登録の手間とか、全然違うところの苦労の方が大きかったりするけど、JavaScriptでコーディングをしてブラウザで実行するくらいなら、誰の許可も要らない。
ガラケーもスマホも、かなり高度な電子計算機なわけだけど、それを計算機として、自分の思い通りに動かして計算させられるってのは、なかなか良い時代なんじゃないかなと思いますね。ブラウザバンザイ。
<script language="JavaScript"> function calc() { // エラー出力領域と結果出力領域を初期化 document.getElementById("result").value = ""; document.getElementById("info").innerHTML=""; // 各設定値を取得する b_yer = document.getElementById("b_yer").value; b_mon = document.getElementById("b_mon").value; b_day = document.getElementById("b_day").value; a_yer = document.getElementById("a_yer").value; a_mon = document.getElementById("a_mon").value; a_day = document.getElementById("a_day").value; // before日付:"yyyy/mm/dd" 形式の文字列を作り、parseして「ミリ秒単位」の数値に変換する bef = Date.parse(b_yer+"/"+b_mon+"/"+b_day); if (isNaN(bef)) { // NaNと判定されたら日付形式不正 document.getElementById("info").innerHTML="<font color='#ff0000' size='-1'>日付指定の形式が不正</font>"; return; } // 「1月32日」などと入力しても適当に補正できるよう、再代入 befdate = new Date(bef); document.getElementById("b_yer").value = befdate.getFullYear(); document.getElementById("b_mon").value = befdate.getMonth()+1; document.getElementById("b_day").value = befdate.getDate(); // after日付。以下コメント略 aft = Date.parse(a_yer+"/"+a_mon+"/"+a_day); if (isNaN(aft)) { document.getElementById("info").innerHTML="<font color='#ff0000' size='-1'>日付指定の形式が不正</font>"; return; } aftdate = new Date(aft); document.getElementById("a_yer").value = aftdate.getFullYear(); document.getElementById("a_mon").value = aftdate.getMonth()+1; document.getElementById("a_day").value = aftdate.getDate(); // 各経過ミリ秒数を割り算して経過日数に変換し、その差を求めて出力する document.getElementById("result").value = aft/1000/60/60/24 - bef/1000/60/60/24; } function init(n) { // 入力値をクリア document.getElementById("b_yer").value = ""; document.getElementById("b_mon").value = ""; document.getElementById("b_day").value = ""; document.getElementById("a_yer").value = ""; document.getElementById("a_mon").value = ""; document.getElementById("a_day").value = ""; document.getElementById("result").value = ""; document.getElementById("info").innerHTML=""; } </script> <input type="text" id="b_yer" size="5" maxlength="4" value="">年 <input type="text" id="b_mon" size="3" maxlength="2" value="">月 <input type="text" id="b_day" size="3" maxlength="2" value="">日~<br> <input type="text" id="a_yer" size="5" maxlength="4" value="">年 <input type="text" id="a_mon" size="3" maxlength="2" value="">月 <input type="text" id="a_day" size="3" maxlength="2" value="">日 <input type="button" value="計算" onclick="calc();"> <input type="button" value="削除" onclick="init();"><br> 経過日数:<input type="text" id="result" size="9" value=""> <span id="info"></span><br><br> <script language="JavaScript"> // 既定値として本日日付をafter日付に設定しておく var hiduke = new Date(); var year = hiduke.getFullYear(); var month = hiduke.getMonth()+1; var day = hiduke.getDate(); document.getElementById("a_yer").value = year; document.getElementById("a_mon").value = month; document.getElementById("a_day").value = day; </script>
ポイントとしては、Date.parse("2015/05/11"); のように、よく知っている日付形式を Date.parse に与えると、1970/1/1 からの経過ミリ秒数を表す整数値が得られるということですね。
そのため、2つの日付からそれぞれ整数値を計算し、差を求め、÷1000(ミリ秒)÷60(秒)÷60(分)÷24(時間) を計算してやれば、経過日数が求まるという算段です。
入力形式が多少おかしい(1月32日)場合でも、JavaScriptは適当に補正(⇒2月1日扱い)してくれるので、補正結果が入力欄に再代入されるようにしてあります。ただ、"A月B日"のような入力には堪えられませんので、その場合はエラー出力します。
エラー出力に際しては、ガラケーではモーダルダイアログを自由に出せない(alert()が使えない)場合がありますので、画面上に赤字でエラー文字列を出すようにしています。
注意点としては、getMonth()は0~11を返すということですかね。0は1月を表し、1は2月を表し、…11は12月を表す。
他の要素、例えば年とか日は、ダイレクトにその数字が表す物になっているのに、月だけは数字が表すものになっていない。ちょっと気持ち悪いですね。
おそらく、英語では月に対してJanuaryとかFebruaryとかの名称が紐尽くから、その名称の配列の番号として適切になるように・・・という配慮なんだと思うのですが、我々日本人からすると、なんだか思想が統一されていなくて気持ち悪いと毎回思ってしまいます。
よく考えてみたら、Date.parse("2015/05/11"); のように、文字列からJavaScriptの実装の柔軟性に頼って日付情報を得るというのは、不確実性が増すばかりで、あまりよろしくない気がしてきました。
海外産のマイナーブラウザとかだと、うまく動かないこともあるかもしれないし。というわけで、この部分は少し作り替えます。
[1]修正前: // before日付:"yyyy/mm/dd" 形式の文字列を作り、parseして「ミリ秒単位」の数値に変換する bef = Date.parse(b_yer+"/"+b_mon+"/"+b_day); [1]修正後: // before日付:Dateで日付を生成し、getTime()で「ミリ秒単位」の数値に変換する dt = new Date(Number(b_yer), Number(b_mon)-1, Number(b_day)); bef = dt.getTime();
[2]修正前: // after日付。以下コメント略 aft = Date.parse(a_yer+"/"+a_mon+"/"+a_day); [2]修正後: // after日付:Dateで日付を生成し、getTime()で「ミリ秒単位」の数値に変換する dt = new Date(Number(a_yer), Number(a_mon)-1, Number(a_day)); aft = dt.getTime();
Number() で数値変換をする関係で、細かいところの動作が変わります。例えば、空白状態だと0が入力されていると判断され、それなりの補正処理が行われます。
●実行結果:必ずしも、携帯電話でJavaScriptが有効になっているとは限りませんので、計算部分はサーバサイドのスクリプトに任せる方法を模索してみます。ここではPHPを使います。
PHPでは、mktime() を使ってタイムスタンプを取得します。このタイムスタンプは、1970年1月1日の午前0時からの経過秒となっているので、タイムスタンプの差分を求めた後に、÷60(秒)÷60(分)÷24(時間) を計算してやれば、日付の差分が求まります。
<form action="h201505a.html#phpresult" method="post"> <?php // 未POST状態かどうかを確認。POSTされていなければ初期状態にする if (isset($_POST["b_yer2"]) && intval($_POST["b_yer2"]) > 0 && isset($_POST["b_mon2"]) && intval($_POST["b_mon2"]) > 0 && isset($_POST["b_day2"]) && intval($_POST["b_day2"]) > 0 && isset($_POST["a_yer2"]) && intval($_POST["a_yer2"]) > 0 && isset($_POST["a_mon2"]) && intval($_POST["a_mon2"]) > 0 && isset($_POST["a_day2"]) && intval($_POST["a_day2"]) > 0) { // ひとまず、POSTされた値を取得。取得するときは数値化する。 $b_yer2 = intval($_POST["b_yer2"]); $b_mon2 = intval($_POST["b_mon2"]); $b_day2 = intval($_POST["b_day2"]); $a_yer2 = intval($_POST["a_yer2"]); $a_mon2 = intval($_POST["a_mon2"]); $a_day2 = intval($_POST["a_day2"]); // Unixタイムスタンプを作成する $bef = mktime(0, 0, 0, $b_mon2, $b_day2, $b_yer2); $aft = mktime(0, 0, 0, $a_mon2, $a_day2, $a_yer2); // afterとbeforeの差分を求める $delta = ($aft - $bef)/60/60/24; // 表示日付の正規化のために、mktimeの結果からもう一度、年月日の数値を作成 $b_yer2 = date("Y", $bef); $b_mon2 = date("m", $bef); $b_day2 = date("d", $bef); $a_yer2 = date("Y", $aft); $a_mon2 = date("m", $aft); $a_day2 = date("d", $aft); // before側 再表示 echo " <input type='text' name='b_yer2' size='5' maxlength='4' value='$b_yer2'>年\n"; echo "<input type='text' name='b_mon2' size='3' maxlength='2' value='$b_mon2'>月\n"; echo "<input type='text' name='b_day2' size='3' maxlength='2' value='$b_day2'>日~<br>\n"; // after側 再表示 echo " <input type='text' name='a_yer2' size='5' maxlength='4' value='$a_yer2'>年\n"; echo "<input type='text' name='a_mon2' size='3' maxlength='2' value='$a_mon2'>月\n"; echo "<input type='text' name='a_day2' size='3' maxlength='2' value='$a_day2'>日\n"; echo "<input type='submit' value='送信'><br>"; // 経過日数の表示 echo " 経過日数:$delta"; } else { // 初期表示。before側は空。 ?> <input type="text" name="b_yer2" size="5" maxlength="4" value="">年 <input type="text" name="b_mon2" size="3" maxlength="2" value="">月 <input type="text" name="b_day2" size="3" maxlength="2" value="">日~<br> <?php // after側は現在日付を表示 echo " <input type='text' name='a_yer2' size='5' maxlength='4' value='".date('Y')."'>年\n"; echo "<input type='text' name='a_mon2' size='3' maxlength='2' value='".date('m')."'>月\n"; echo "<input type='text' name='a_day2' size='3' maxlength='2' value='".date('d')."'>日\n"; ?> <input type="submit" value="送信"><br> <?php } ?>●実行結果:
最近あまり使っていないPerlですが、思い出しがてら作ってみました。エラー処理や、ヘッダ出力などの周辺処理は省略しています。送信しているクエリの名称や、送信元のフォームについては、このページのHTMLソースを参照ということで。