JavaScript だとハマる 〜生年月日から年齢を計算する簡単な計算式〜

生年月日から年齢を計算する簡単な計算式に関して。

(今日の日付 - 誕生日) / 10000 の小数点以下切捨て


という式で簡単に生年月日から年齢を簡単に算出できるとのこと。目から鱗


で、PHPJavaScript で実装してみたら、 JavaScript では実は全然簡単ではないことが判明!


※ 生年月日は yyyy-mm-dd 形式でDB保存されていることが多いと思うので、yyyy-mm-dd 形式から計算することにしました。


まず、PHP

// PHP だと簡単
/**
 * yyyy-mm-dd 形式の誕生日から年齢を計算
 */
function calculateAge($birthday)
{
	$birthday = intval(str_replace('-', '', $birthday));
	$today    = intval(date('Ymd'));
	return intval(($today - $birthday) / 10000);
}

// 簡単なテストケース
echo calculateAge('1977-07-07');

さて、問題のJavaScript

/**
 * yyyy-mm-dd 形式の誕生日から年齢を計算
 */
function calculateAge(birthday) {
	var  birth = birthday.split('-'); // birth[0]: year, birth[1]: month, birth[2]: day
	var _birth = parseInt("" + birth[0] + birth[1] + birth[2]);// 文字列型に明示変換後にparseInt
	var  today = new Date();
	var _today = parseInt("" + today.getFullYear() + affixZero(today.getMonth() + 1) + affixZero(today.getDate()));// 文字列型に明示変換後にparseInt
	return parseInt((_today - _birth) / 10000);
}

/**
 * 1, 2 など 1桁の数値を 01, 02 などの文字列に変換(2桁以上の数字は単純に文字列型に変換)
 */
function affixZero(int) {
	if (int < 10) int = "0" + int;
	return "" + int;
}

/**
 * javascript では 5 + 6 + 1 は 12 となりますが、 "" + 5 + 6 + 1 と文字列を一つ含ませると "561" という文字列として結合されます。(たぶん全ブラウザ対応だと...。)
 */ 

// 簡単なテストケース
alert(calculateAge('1977-07-07'));

[追記] 上のソースはjavascriptの学習にはなるかもしれないので、そのままにしておきますが、 nobody6 よりコメントいただいたソースの方が断然実用的ですので参考にしてください。(コメントでブラッシュアップされたりするって素敵ですね!)
もう一点、学習効果という面では、私のコードの方のイケてない原因が PHP のロジック(文字列→整数化)に javascript を当てこんだでしまったことにあります。それぞれの言語に最適なロジックを用いることも大切なことですよね!




私のコーディング能力の問題??


それもあるでしょう。(JavaScript は好んで使わないし、と言い訳)


では、なぜこんなソースになったか。


1. javascript の Date オブジェクトのメソッドは 01 月のような mm 形式の月を簡単に得られないため、自前で用意する必要がある(らしい)
2. javascript の Date.getMonth() メソッドの返り値は 0 〜 11 である!(地味にハマる)ちなみに、0 は一月、1 は二月を意味する
3. Date.getYear() だと 2007 は返ってこない(地味にハマる)。正解は Date.getFullYear()


要するに Date オブジェクトから yyyymmdd形式の日付を得るのにハマるということです。


(違うアルゴリズムから算出すれば話は別ですが)他に良いコードがあるかな?


※違うアルゴリズムとしては、まず西暦年を引き算して、その年の誕生日を迎えていなければ1ひくというのがあります。EC CUBE はこのアルゴリズムだったような…。(うろ覚え)



[追記]

javascript なら別アルゴリズムに基づく以下の方がすっきりするかも。

/** javascript 特有の注意事項
 *  1. javascript の Date.getMonth() メソッドの返り値は 0 〜 11 であることに注意!
 *     => 0 は一月、1 は二月を意味する
 *  2. parseInt() は第2引数に 10進数であることを明示しないと "08", "09" で 0 が返る仕様となっていることに注意!
 *     => 予期せぬバグを未然に防ぐためにも常に第2引数を指定する癖をつけたほうがいいかもしれません。
 *     => http://www.google.co.jp/search?q=javascript+parseInt+08&btnG=%E6%A4%9C%E7%B4%A2&hl=ja
 */

/**
 * @param birthday: yyyy-mm-dd 形式の誕生日
 */
function calculateAge(birthday) {
	var birth = birthday.split('-');
	var today = new Date();
	if ( parseInt(birth[1], 10) * 100 + parseInt(birth[2], 10) > (today.getMonth() + 1) * 100 + today.getDate() ) {
		return today.getFullYear() - parseInt(birth[0], 10) - 1;
	}
	return today.getFullYear() - parseInt(birth[0], 10);
}