日付書式にマッチさせる(3)

前回で終わるはずだった日付書式正規表現リファラを見てみると「日付 正規表現 400」こんな検索式でぐぐってる人がいて感心しました.なるほど,うるう年を真面目に扱おうとすると400って数字が出ますからね.逆にこの数字が出ないと期待外れの結果が得られやすいかも?
この検索結果から,以下の2つを見つけました.

http://oraclesqlpuzzle.hp.infoseek.co.jp/regex/regex-4-3.html

^(?!([02468][1235679]|[13579][01345789])000229)(
([0-9]{4}(01|03|05|07|08|10|12)(0[1-9]|[12][0-9]|3[01]))|
([0-9]{4}(04|06|11)(0[1-9]|[12][0-9]|30))|
([0-9]{4}02(0[1-9]|1[0-9]|2[0-8]))|
([0-9]{2}(([02468])[048]|[13579][26])0229))$

blog/2005年05月09日/日付チェックの正規表現

^(?:\d{4}/(?:(?:0?[1-9]|1[0-2])/(?:0?[1-9]|1[0-9]|2[0-8])| 
(?:0?[13-9]|1[0-2])/(?:29|30)|(?:0?[13578]|1[02])/31)| 
(?:\d{2}(?:0[48]|[2468][048]|[13579][26])| 
(?:[02468][048]|[13579][26])00)/0?2/29)$

最初の例は否定の先読みを使っていますね.えーと,([02468][1235679]|[13579][01345789])というのは4の倍数ではない2桁の数値ですね.十の位が偶数のときは一の位が0,4,8のときに4の倍数,十の位が奇数のときは一の位が2,6のときに4の倍数ですから全体としてみると4の倍数ではない2桁の数値になります.なので(?!([02468][1235679]|[13579][01345789])000229)で,400では割り切れないけど100で割り切れる年の2月29日はダメってことになります.あとは大の月,小の月,2月で場合分けですね.
2番目の例は視点が違いますね.とりあえず28日までは,月やうるう年を問わずOK.2月以外は29日と30日はOKで,31日は大の月だけOK.そして下2桁が4の倍数か,上2桁が4の倍数で下2桁が00の2月は29日もOKという場合分けをされています.エレガントですね.
どちらの例も自分の書いた正規表現よりも短く,さらに.NET Framework正規表現以外にも対応してそうなので数段優れてますね.特に2番目のほうはパフォーマンス的にも優れてそうだし,yyyy/MM/dd形式にもyyyy/M/d形式にも対応しててすばらしいです.
……って,あれ?EntLibのにしざきさんだったんですね.さすが!