問題意識
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