問題意識
PHPが表現できる日付に制限があり、1800年といった表記をdate関数から
出力できないため、1800年から2500年程度の時間表現を行う方法を導く。
実験内容
まず、制限値の詳細を知るために
1.date関数の第2引数に与えられるタイムスタンプの範囲を調べる。
2.strtotime関数の引数に指定できる時間の範囲を調べる。
実験結果
1.date関数の第2引数にタイムスタンプとして指定した場合:
最小値:-2147483648(1901-12-14 05:45:52)
最大値: 2147483647 (2038-01-19 12:14:07)
2.strtotime関数の引数に時間を指定した場合
最小値: 1901-12-14 05:45:52
最大値: 2038-01-19 12:14:07
まとめ
32ビット環境において、
PHPで整数を扱う場合は-2^31から2^31-1しか扱えない。
次の問題意識
pearのDateクラスは有効か調査する。
実験内容
2.1 コンストラクタに2147483647を指定し、
秒を追加してゆき、getDateを表示してみて
想定する結果との差異を検証。
2.2
コンストラクタに-2147483648を指定し、
秒を減らしてゆき、getDateを表示してみて
想定する結果との差異を検証。
実験結果
2.1
9999-12-31 23:59:59 まで表示可能その後に1秒を足すと現在時刻に初期化される。
Date.phpのソースコードを追ってみると、年・月・日や時・分・秒はそれぞれ変数になっている。
なぜ、999年までしかいけないかというと、addSpanメソッドで無理矢理4桁に処理されているため。
2.2
999-12-31 23:59:59 まで表示可能。その後に1秒を引くと9909-12-31 23:59:58となる
まとめ
西暦が4桁ならば正常に動作。
次の問題意識
3.1 mktimeのサポートする範囲
実験結果
3.1
年の入力に制限がある。
年の入力範囲:0-38,70-110,1903-2038
まとめ
mktimeは1903年から2038年まで。
次の問題意識
4.checkdateのサポートする範囲
実験結果
4.西暦1年1月1日から、西暦32767年12月31日
まとめ
西暦は16ビット分。チェックには利用可能。
次の問題意識
mktime,strtotimeが表現できる日時に制限があるため、目的の範囲を
サポートする日時のvalidation方法はあるのか。
調査
・symphonyはmktime,strtotimeを利用してチェック。
・zend frameworkはcheckdateにて日付をチェック。
・Mapleはcheckdateにて日付のみチェック。
結論
PHPのネイティブ関数でサポートする日時表記の積集合部分は
1901-12-14 05:45:52から2038-01-19 12:14:07である。
日付のチェックはcheckdateを使えば実用に問題はない。
時間のチェックは自分で書く。
ネイティブでサポートされていない日時表記のためにはPearのDateクラスを利用すれば
1000年から9999年まで表現できる。
<?php /** * 実験1 * date関数の第2引数がサポートするタイムスタンプの最大値、最小値を求める。 * */ require_once('Date.php'); print 'result:'.getMax(2147558400 - 60*60*24, 1); function getMax($from = 0 , $step = 1){ $i = $from; $last_i = 0; while(true){ $current = date('Y-m-d H:i:s', $i); if($current == $last){ return $last_i; } if(($i % $step) == 0){ print sprintf("%s %d\n", $current, $i); } $last_i = $i; $last = $current; // increment $i += $step; } }
<?php /** * 2.Dateクラスの範囲測定 * */ require_once('Date.php'); //$date = new Date(2147483647); $date = new Date(-2147483647); for($i=0;$i < 902;$i++){ $date->addSeconds(-60*60*24*365); } $date->addSeconds(-60*60*24*200); $last = ''; while(true){ $current = $date->getDate(); if($current == $last){ var_dump($current); var_dump($date); break; } var_dump($current); $date->addSeconds(-1); $last = $current; }
<?php /** * 実験3 * mktimeがサポートする範囲 * */ for($i=0;$i<10000;$i++){ var_dump(mktime(0,0,0,0,0,$i)); print $i."\n"; }
<?php /** * 実験4 * checkdateがサポートする範囲 * */ var_dump(checkdate(1,1,-100)); var_dump(checkdate(1,1,1)); var_dump(checkdate(1,1,10)); var_dump(checkdate(1,1,1969)); var_dump(checkdate(1,1,2039)); var_dump(checkdate(1,1,9999)); var_dump(checkdate(1,1,10000)); var_dump(checkdate(12,31,32767));
Comments