878 文字
4 分
PHPの日時に関連する関数の制限

問題意識#

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));
PHPの日時に関連する関数の制限
https://blog.teraren.com/posts/php-date-function/
作者
Yuki Matsukura
公開日
2007-11-23
ライセンス
CC BY-NC-SA 4.0

コメント