日付・時刻の形式チェック

<dl>
<dt>isMatch_YYYYMMDD</dt>
<dd>日付が西暦4桁、月2桁、日2桁の形式かつ有効な日付かどうか</dd>
<dt>isMatch_HHMMSS</dt>
<dd>時刻が時2桁、分2桁、秒2桁の形式かつ有効な時刻かどうか</dd>
<dt>isMatchDateTime</dt>
<dd>上2つを確認する。日付と時刻は半角空白区切り</dd>
</dl>

* preg_matchを使用してチェックしている
* $_exclusive_date_list変数で指定した形式の日付は有効な日付とする

```php
/**
 * 日付チェックを除外する日付のリスト
 * @var array
 */
$_exclusive_date_list = array('00000000');

/**
 * 日付が西暦4桁、月2桁、日2桁の形式かどうか確認する
 * @param  string  $text         確認する文字列
 * @param  string  $delim        日付区切り文字。/を区切り文字とするときは'\/'とエスケープ文字を入れる
 * @param  boolean $errorIfEmpty 空文字列をエラーとするか TRUE:する FALSE:しない
 * @return boolean               有効な日付フォーマットかどうか TRUE:有効 FALSE:有効でない
 */
function isMatch_YYYYMMDD($text, $delim = '-', $errorIfEmpty = TRUE) {
	global $_exclusive_date_list;
	
	#空文字チェック
	if($text == ''){
		return !$errorIfEmpty;
	}

	#年 0000-9999 月 00-99 日 00-99
	$pattern = '/^([0-9]{4})' . $delim . '([0-9]{2})' . $delim . '([0-9]{2})$/';
	$matches = array();
	$match = isMatch($pattern, $text, $errorIfEmpty, $matches);
	if(!$match) {
		return FALSE;
	}
	
	#日付チェックを除外するかチェック
	if(in_array($matches[1] . $matches[2] . $matches[3], $_exclusive_date_list)) {
		return TRUE;
	}

	#　日付存在するかチェック
	if(!checkdate((int)$matches[2], (int)$matches[3], (int)$matches[1])) {
		return FALSE;
	}

	return TRUE;
}

/**
 * 時刻が時2桁、分2桁、秒2桁の形式かどうか確認する
 * @param  string  $text         確認する文字列
 * @param  string  $decimal      秒未満の桁数
 * @param  string  $delim        時刻区切り文字
 * @param  boolean $errorIfEmpty 空文字列をエラーとするか TRUE:する FALSE:しない
 * @return boolean               有効な時刻フォーマットかどうか TRUE:有効 FALSE:有効でない
 */
function isMatch_HHMMSS($text, $decimal = 0, $delim = ':', $errorIfEmpty = TRUE) {
	#空文字チェック
	if($text == ''){
		return !$errorIfEmpty;
	}

	//うるう秒はMySQLでは登録できないためフォーマットエラーとする。
	//対応する場合は下の正規表現を使用する
	//#時 00-23 分 00-59 秒 00-60
	//$pattern = '/^(0[0-9]{1}|1{1}[0-9]{1}|2{1}[0-3]{1})' . $delim . '(0[0-9]{1}|[1-5]{1}[0-9]{1})' . $delim . '(0[0-9]{1}|[1-5]{1}[0-9]{1}|60)';
	#時 00-23 分 00-59 秒 00-59
	$pattern = '/^(0[0-9]{1}|1{1}[0-9]{1}|2{1}[0-3]{1})' . $delim . '(0[0-9]{1}|[1-5]{1}[0-9]{1})' . $delim . '(0[0-9]{1}|[1-5]{1}[0-9]{1})';

	# 小数部があればパターンに追加
	if($decimal > 0) {
		$pattern = $pattern . sprintf('\.([0-9]{%d})', $decimal);
	}
	$pattern = $pattern . '$/';

	return isMatch($pattern, $text, $errorIfEmpty);
}

/**
 * 日時フォーマットかどうか確認する
 * 日付と時刻の間は半角空白で区切られていること
 * @param  string  $text         確認する文字列
 * @param  string  $decimal      秒未満の桁数
 * @param  boolean $errorIfEmpty 空文字列をエラーとするか TRUE:する FALSE:しない
 * @param  string  $date_delim   日付区切り文字。/を区切り文字とするときは'\/'とエスケープ文字を入れる
 * @param  string  $time_delim   時刻区切り文字
 * @return boolean               有効な日時フォーマットかどうか TRUE:有効 FALSE:有効でない
 */
function isMatchDateTime($text, $decimal = 0, $errorIfEmpty = TRUE, $date_delim = '-', $time_delim = ':') {
	#空文字チェック
	if($text == ''){
		return !$errorIfEmpty;
	}

	# 日付と時刻に分解
	$datetime = explode(' ', $text);
	if(count($datetime) < 2) {
		return FALSE;
	}

	# 日付
	if(!isMatch_YYYYMMDD($datetime[0], $date_delim)) {
		return FALSE;
	}

	# 時刻
	if(!isMatch_HHMMSS($datetime[1], $decimal, $time_delim)) {
		return FALSE;
	}

	return TRUE;
}

/**
 * 正規表現パターン検索
 * @param  string  $pattern      正規表現
 * @param  string  $text         確認する文字列
 * @param  boolean $errorIfEmpty 空文字列をエラーとするするか TRUE:する FALSE:しない
 * @return boolean               正規表現に合致するか TRUE:合致する FALSE:合致しない
 */
function isMatch($pattern, $text, $errorIfEmpty = TRUE, &$matches = array()) {
	#空文字チェック
	if($text == ''){
		return !$errorIfEmpty;
	}

	if(!preg_match($pattern, $text, $matches)) {
		return FALSE;
	} else {
		return TRUE;
	}
}
```

テストコード（全分岐網羅していない）

```php
function testisMatch_YYYYMMDD($date, $exists = TRUE, $delim = '\/'){
    if(isMatch_YYYYMMDD($date, $delim, $exists))
        {echo '<font color="blue">'.$date.' 正しい日付</font><br/>';}
    else
        {echo '<font color="red">'.$date.' 不正な日付</font><br/>';}
}

function testisMatch_HHMMSS($time, $decimal = 0, $delim = ':'){
    if(isMatch_HHMMSS($time, $decimal, $delim))
        {echo '<font color="blue">'.$time.' 正しい時刻</font><br/>';}
    else
        {echo '<font color="red">'.$time.' 不正な時刻</font><br/>';}
}

echo '<h4>年が不正</h4>';
testisMatch_YYYYMMDD('/01/01'); #値なし
testisMatch_YYYYMMDD('200/01/01'); #桁不足

echo '<h4>月が不正</h4>';
testisMatch_YYYYMMDD('2012//01'); #値なし
testisMatch_YYYYMMDD('2012/1/01'); #桁不足
testisMatch_YYYYMMDD('2012/00/01'); #無効な月
testisMatch_YYYYMMDD('2012/13/01'); #無効な月

echo '<h4>日が不正</h4>';
testisMatch_YYYYMMDD('2012/01/'); #値なし
testisMatch_YYYYMMDD('2012/01/1'); #桁不足
testisMatch_YYYYMMDD('2012/01/00'); #無効な日
testisMatch_YYYYMMDD('2012/01/32'); #無効な日
testisMatch_YYYYMMDD('2014/02/29'); #無効な日

echo '<h4>0000/00/00はOK</h4>';
testisMatch_YYYYMMDD('0000/00/00');
 
echo '<h4>1000日チェック</h4>';
for($i = 0; $i <= 1000; $i++){
    $date = date('Y/m/d',mktime(0,0,0,date('n'),date('j')+$i,date('Y')-1));
    testisMatch_YYYYMMDD($date);
}

echo '<h4>年が不正（区切りなし）</h4>';
testisMatch_YYYYMMDD('0101', TRUE, ''); #値なし
testisMatch_YYYYMMDD('1000101', TRUE, ''); #桁不足

echo '<h4>全部ゼロはOK</h4>';
testisMatch_YYYYMMDD('00000000', TRUE, '');
echo '<h4>うるう年はOK</h4>';
testisMatch_YYYYMMDD('20120229', TRUE, ''); #2/29

echo '<h4>1000日チェック（区切りなし）</h4>';
for($i = 0; $i <= 1000; $i++){
    $date = date('Ymd',mktime(0,0,0,date('n'),date('j')+$i,date('Y')-1));
    testisMatch_YYYYMMDD($date, TRUE, '');
}

echo '<h4>時が不正</h4>';
testisMatch_HHMMSS(':59:59'); #値なし
testisMatch_HHMMSS('0:59:59'); #桁不足
testisMatch_HHMMSS('24:59:59'); #無効な時


echo '<h4>秒が不正</h4>';
testisMatch_HHMMSS('00:00:'); #値なし
testisMatch_HHMMSS('00:00:0'); #桁不足
testisMatch_HHMMSS('00:00:60'); #うるう秒はNG

echo '<h4>ミリ秒が不正</h4>';
testisMatch_HHMMSS('00:00:00.'); #値なし
testisMatch_HHMMSS('00:00:00.01'); #桁不足

echo '<h4>30時間チェック</h4>';
for($i = 0; $i <= 30; $i++){
    $time = date('H:i:s',mktime(0+$i,0,0,date('n'),date('j'),date('Y')));
    testisMatch_HHMMSS($time);
}
echo '<h4>100分チェック</h4>';
for($i = 0; $i <= 100; $i++){
    $time = date('H:i:s',mktime(0,0+$i,0,date('n'),date('j'),date('Y')));
    testisMatch_HHMMSS($time);
}
echo '<h4>100秒チェック</h4>';
for($i = 0; $i <= 100; $i++){
    $time = date('H:i:s',mktime(0,0,0+$i,date('n'),date('j'),date('Y')));
    testisMatch_HHMMSS($time);
}

echo '<h4>100ミリ秒チェック</h4>';
$millitime = date('H:i:s',mktime(0,0,0,date('n'),date('j'),date('Y')));
for($i = 0; $i <= 100; $i++){
    $time = date('H:i:s',mktime(0,0,0+$i,date('n'),date('j'),date('Y')));
    testisMatch_HHMMSS($millitime . '.' . sprintf('%03d', $i),3);
}

```

> [preg_match()正規表現チェッカー ver2.0](http://okumocchi.jp/php/re.php)

> [正規表現 時刻 の書式の判別 「preg_match()」](http://www.tryphp.net/2012/03/14/phpsample-preg-time/)