Written in Japanese(UTF-8)
2015. 6.20
INASOFT

/トップ/目次/管理人のひとこと(ブログ)/2つの日付の経過日数を計算するプログラム

3209421 (+0092)[+0330]

管理人のふたこと Tweet

2つの日付の経過日数を計算するプログラム(JavaScript/PHP/Perl/Ruby/Python)



公開日:2015/05/11 JavaScript
追記 :2015/05/12 JavaScriptその2
追記 :2015/05/17 PHPの場合 - 必ずしも携帯電話でJavaScriptが有効とは限らないので
追記 :2015/05/23 Perlの場合
追記 :2015/05/26 Rubyの場合
追記 :2015/06/20 Pythonの場合

「○○ちゃんが生まれてから、今日で何日目だか、数えていたんだけど分からなくなっちゃった」と、生後何百日目だかの幼児のママから相談がありました。

そういうプログラムを一般のプログラミング言語作るのは、さほど難しくないし、プログラムせずともExcelで計算することもできてしまう。

Excelで日数計算。マクロを使わずとも計算可能
▲Excelで日数計算。これくらいならマクロを書かなくても計算可能である。

とはいえ、幼児のママが、たかが日数計算如きでそんなにホイホイとパソコンを立ち上げてプログラムを起動するわけにもいかないし、Excelが使えるわけでもない。

ただ、ふだんから片手で携帯電話(ガラケー)をいじって、Webサイトを見たりメールを打ったりすることはできるので、今回のソリューションもその範囲でどうにかするのが良いんじゃないかな、と。


■JavaScriptのプログラム

考えてみると、今は凄い時代なので、ガラケーだろうと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>

2つの年月日を入れ、「計算」ボタンを押下すると、「経過日数」の欄に経過日数が表示されます。
「削除」ボタンを押下すると、入力した日付がクリアされます。
おまけ機能として、デフォルトで、2つ目の年月日に本日日付が入るようになっています。

●実行結果:

 日~
 
 経過日数:

ポイントとしては、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とかの名称が紐尽くから、その名称の配列の番号として適切になるように・・・という配慮なんだと思うのですが、我々日本人からすると、なんだか思想が統一されていなくて気持ち悪いと毎回思ってしまいます。


■JavaScriptのプログラム その2

よく考えてみたら、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が入力されていると判断され、それなりの補正処理が行われます。

●実行結果:

 日~
 
 経過日数:


PHPのプログラム

必ずしも、携帯電話で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 "&nbsp;<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 "&nbsp;<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 "&nbsp;経過日数:$delta";
  }
  else {
    // 初期表示。before側は空。
?>
&nbsp;<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 "&nbsp;<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の場合

最近あまり使っていないPerlですが、思い出しがてら作ってみました。エラー処理や、ヘッダ出力などの周辺処理は省略しています。送信しているクエリの名称や、送信元のフォームについては、このページのHTMLソースを参照ということで。

#!/(Perlの位置を記述) use Time::Local; use CGI; # (ヘッダ出力などは省略) # postメソッドにより入力したクエリの受け取り read( STDIN, $buf, $ENV{'CONTENT_LENGTH'} ); # 初期化 my $ptext = ""; my $cgi = CGI->new; $cgi->charset("utf-8"); # フォームに必ず含まれる文字列を検定 if ($buf =~ /b_yer3.*/s and $buf =~ /b_mon3.*/s and $buf =~ /b_day3.*/s and $buf =~ /a_yer3.*/s and $buf =~ /a_mon3.*/s and $buf =~ /a_day3.*/s) { # クエリをアンバサントで分割する。 my @pairs = split(/&/,$buf); foreach $pair (@pairs) { # キーと値に分割する(key=value&key=value...)。 my($name, $value) = split(/=/, $pair); # '+' → ' ' $value =~ tr/+/ /; # '\r\n' → '\n' $value =~ s/%0D%0A/%0A/ig; # 16進数にエンコードされた文字列をデコードする。 $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; # クエリをクエリ格納用変数へ格納 if ( $name eq "b_yer3" ) { $b_yer3 = $value; } elsif ( $name eq "b_mon3" ) { $b_mon3 = $value; } elsif ( $name eq "b_day3" ) { $b_day3 = $value; } elsif ( $name eq "a_yer3" ) { $a_yer3 = $value; } elsif ( $name eq "a_mon3" ) { $a_mon3 = $value; } elsif ( $name eq "a_day3" ) { $a_day3 = $value; } } # beforeの経過秒を求める $b_time = timelocal(0, 0, 0, $b_day3, $b_mon3 - 1, $b_yer3); # afterの経過秒を求める $a_time = timelocal(0, 0, 0, $a_day3, $a_mon3 - 1, $a_yer3); # 差分を計算 $delta = ($a_time - $b_time)/60/60/24; print $b_yer3."年".$b_mon3."月".$b_day3."日 ~ ".$a_yer3."年".$a_mon3."月".$a_day3."日までの経過日数は ".$delta."日です。\n"; }
●実行結果
 日~
 

Rubyの場合

日本人なのでRubyでも作ってみました。エラー処理や、ヘッダ出力などの周辺処理は省略しています。送信しているクエリの名称や、送信元のフォームについては、このページのHTMLソースを参照ということで。

#!/(Rubyの位置を記述)

require 'cgi'  # CGIライブラリを読み込み
require 'date'

cgi = CGI.new  # 初期化

print cgi.header("type"=>"text/html")
# (ヘッダ出力などは省略)

b_yer3 = cgi["b_yer3"]
b_mon3 = cgi["b_mon3"]
b_day3 = cgi["b_day3"]
a_yer3 = cgi["a_yer3"]
a_mon3 = cgi["a_mon3"]
a_day3 = cgi["a_day3"]

bef = Date.new(b_yer3.to_i, b_mon3.to_i, b_day3.to_i)
aft = Date.new(a_yer3.to_i, a_mon3.to_i, a_day3.to_i)
delta = (aft - bef).to_i

print(b_yer3,"年",b_mon3,"月",b_day3,"日 ~ ",a_yer3,"年",a_mon3,"月",a_day3,"日の経過日数は",delta,"日です。\n")

●実行結果
 日~
 

Pythonの場合

最近、Boost.Pythonの力を借りることで、プログラム内スクリプトを意外と簡単に実現できるのではないかということを知り、自分の中でマイブームが発生しそうな言語です。
今回はその話題は置いておいて、本題の方を書きます。

プログラム内スクリプトの件は、また後日、書きます。

#!/(Pythonの位置を記述)
# -*- coding: utf-8 -*-

import cgi
import os
import datetime

print "Content-Type: text/html\n\n"

# HTMLヘッダの出力は省略

if os.environ['REQUEST_METHOD'] == "POST":
  form = cgi.FieldStorage()

  # POSTされた文字列の取得
  b_yer3 = int(form["b_yer3"].value)
  b_mon3 = int(form["b_mon3"].value)
  b_day3 = int(form["b_day3"].value)
  a_yer3 = int(form["a_yer3"].value)
  a_mon3 = int(form["a_mon3"].value)
  a_day3 = int(form["a_day3"].value)

  print b_yer3,"年",b_mon3,"月",b_day3,"日 ~ ",a_yer3,"年",a_mon3,"月",a_day3,"日までの経過日数は"

  # 差分計算
  bef = datetime.date( b_yer3, b_mon3, b_day3 )
  aft = datetime.date( a_yer3, a_mon3, a_day3 )
  delta = aft - bef

  print delta.days,"日です。\n"
●実行結果
 日~
 

本ページへは、自己責任の範囲内であれば自由にリンクしていただいて構いません。
本ページに掲載されている内容は、自由にお使いいただいて構いませんが、必ずしも筆者が内容を保証するものではありませんので、ご利用に際しては自己の責任においてお使いいただきますよう、お願いいたします。
このページのURLやアンカーは、サーバ運営・サイト運営・ページ運営・その他の都合により無告知で一時的あるいは永遠に消滅したり、変更したりする可能性がありますので、あらかじめご了承下さい。
本ページは、公開から1年半経過後の任意のタイミングで削除される予定です。本ページの内容は複製・公開していただいて構いません。


/トップ/目次/管理人のひとこと(ブログ)/2つの日付の経過日数を計算するプログラム