正規表現で年月日を分解する

(1/1)
西暦年月日を表すには様々な書式がある――「2007/1/4」「2007-01-04」「2007年1月4日」‥‥すべて西暦年月日を表している。今回は、正規表現を使い、これら様々な日付表現から年・月・日を分解する方法を紹介する。

(2021年8月1日)PHP8対応,リファラ・チェック追加.

目次

サンプル・プログラムの実行例

正規表現で年月日を分解する

サンプル・プログラム

圧縮ファイルの内容
date2yyyymmdd.phpサンプル・プログラム本体

日付の表現形式

月の名をアルファベットで表記する欧米方式や、和暦まで対応するのは大変なので、ここでは、「年」「月」「日」の順で数字が並んでいる書式に限ることにする。
かならず年月日の3つ組でなければならない。
数字は半角でも全角でも構わないが、漢数字は対応しない。
また、区切り文字は「/」「-」「.」「年」「月」「日」で、半角・全角ともに対応する。

これを正規表現で表現すると、
/([0-90-9]{1,4})[\/\-\.\/-.年]{1}([0-90-9]{1,2})[\/\-\.\/-.月]{1}([0-90-9]{1,2})[\/\-\.\/-.日]?$/iu
となる。順に説明していこう。

今回使った正規表現

(...)
サブパターン。 マッチングだけであれば不要だが、置換を行うために、マッチした部分文字列に番号を付けてやる必要がある。その場合にサブパターンを用いる。
[...]
文字クラス。 この中に記述された文字の並びのうちの1文字を表す。
-
文字の範囲指定。 0-9 は半角数字に、0-9は全角数字にマッチする。
{a,b}
直前文字の a 回以上 b 回以下の繰り返し。bは省略できる。 年は4桁以下、月と日は2桁以下の数字になるので、これを用いた。
?
直前1文字の0または1回の繰り返し。
$
文字列の終端を表す。
i
修飾子。パターンの中の文字は大文字にも小文字にもマッチする。
u
修飾子。パターンと対象文字列はUTF-8として処理する。

解説:文字コードと正規表現

0010: // 初期化処理 ================================================================
0011: define('INTERNAL_ENCODING', 'UTF-8');
0012: mb_internal_encoding(INTERNAL_ENCODING);
0013: mb_regex_encoding(INTERNAL_ENCODING);

今回は、入力や正規表現処理に UTF-8 を使うので、関数  mb_internal_encoding   mb_regex_encoding  で UTF-8 を指定する。
PHP の仕様上は関数  mb_internal_encoding  だけで十分であるが、念のため、 mb_regex_encoding  も指定した。

なお、PHP のマルチバイト文字列関数では、「JIS, SJIS, ISO-2022-JP, BIG-5」は「動作しないと思われる」とあるので、UTF-8 を用いることにした。PHP のマルチバイト系正規表現エンジンは、PHP4.x では Ruby のものを、PHP5.x では「鬼車」(Ruby 1.9.0 で搭載予定)を利用しているので、正規表現に限っては SJIS に対応している可能性は高いのだが、念のため UTF-8 を使うことにした。

0036: //日付表示の分解パターン
0037: $pat  = '/([0-90-9]{1,4})[\/\-\.\/-.年]{1}([0-90-9]{1,2})[\/\-\.\/-.月]{1}([0-90-9]{1,2})[\/\-\.\/-.日]?/iu';

正規表現は、あらかじめ変数 $pat に格納しておく。

解説:分解処理

0206: //テキストを分解
0207: if (preg_match($pat$sour$arr) == 1) {
0208:     if (count($arr) != 4) {       //分解に失敗
0209:         $yyyy = 'error!';
0210:         $mm   = 'error!';
0211:         $dd   = 'error!';
0212:     } else {
0213:         $yyyy = intval(mb_convert_kana($arr[1], 'n'));
0214:         if ($yyyy < 100)    $yyyy += 2000;        //西暦2桁なら2000を加算
0215:         $mm   = intval(mb_convert_kana($arr[2], 'n'));
0216:         $dd   = intval(mb_convert_kana($arr[3], 'n'));
0217:     }
0218: 
0219: //分解に失敗
0220: else {
0221:     $yyyy = 'error!';
0222:     $mm   = 'error!';
0223:     $dd   = 'error!';
0224: }

今回はマルチバイト文字による正規表現を利用するので、関数  preg_match  によってテキストを分解し、配列変数 $arr に格納する。

年月日の3つ組が入力されていることが前提なので、入力された文字列自身($arr[0])を含め、配列変数 $arr の要素数は必ず4になるはずである。そこで、関数  count  を使って要素数をチェックする。そうでなければ、変数に文字列 "error!" を代入する。

正常に3つに分解できたら、関数  mb_convert_kana  関数で全角数字を半角数字に変換し、関数  intval  で整数化する。
また、西暦年が2桁の場合は、自動的に 2000 を加算するようにした。

必要に応じて、分解された年月日が正しい値かどうかチェックする仕組みも追加するといいだろう(例:2月30日はエラー)。

0191: //入力パラメータ
0192: $sour = getParam('sour', FALSEdate('Y/m/d'));        //元のテキスト
0193: $sour = htmlspecialchars($sour);                 //XSS対策
0194: $yyyy = getParam('yyyy', FALSE, 0);                 //年
0195: $mm   = getParam('mm',   FALSE, 0);                 //月
0196: $dd   = getParam('dd',   FALSE, 0);                 //日

XSS 対策のため、入力された日付は関数  htmlspecialchars  でエスケープしてある。
(この項おわり)
header