CakePHPで日付のバリデーションが気になったので調べてみました

CakePHPで日付のバリデーションが気になったので調べてみました

みなさんごきげんよう。yamashitaです。
ラーメンとつけ麺の話ばかりしていたら「ラーメンの記事でも書いたら?」と周囲から言われています。
味の感想は「おいしい」と「とてもおいしい」の二択なので難しいですね。

さて今回はバリカタ・・・ではなくCakePHPのバリデーションについてです。
といってもあまり実用的ではないので軽い気持ちで読んでください。

きっかけ

先日パイセンが作ったwebページを恐れ多くも検証しました。
生年月日の項目に偉人の生まれや大きな出来事の日付を入れてみたらバリデーションに引っかからず、面白くなっていくつか試してみました。
しかし、織田信長が討たれた本能寺の変、1582年6月21日はバリデーションに引っかかったのです。
※いちごパンツのムーニーマンと覚えてください。

おや?と思い、何となく予想を立てて以下の二つを入力するとこうなりました。
- 1600年1月1日⇒OK
- 1599年12月31日⇒NG
なんとも不可解なバリデーションルールだ・・・
西暦だけ見ると「おのれ家康ー!過去を無かったことにするつもりかー!」と言いたくなります。

ソースを見てみるとCakePHPで用意されたバリデーションを使ってました。

 'birthday' => [
                 'date' => [
                   'rule' => [
                       'date',
                       'ymd',
                   ],
                   'message' => '生年月日がおかしいよ',
                 ]
               ]

'date'とは一体どんなルールなのだろうか

今回参考にするCakeのバージョンは2.10.3になります。
これがCakePHPのバリデーションファイル、そしてdate関数です。
\cakephp\lib\Cake\Utility\Validation.php
ソースはGitHubで確認してください

気付いたことを書き出していきたいと思います。

初っ端からコメントで嘘をつかないでください

 * Years are valid from 1800 to 2999.

と思いましたが西暦(y)だけ、年月(ym,my)だけだと間違っていないみたいです。

$month = '(0[123456789]|10|11|12)';
$separator = '([- /.])';
$fourDigitYear = '(([1][8-9][0-9][0-9])|([2][0-9][0-9][0-9]))';
$twoDigitYear = '([0-9]{2})';
$year = '(?:' . $fourDigitYear . '|' . $twoDigitYear . ')';
//中略
$regex['my'] = '%^(' . $month . $separator . $year . ')$%';
$regex['ym'] = '%^(' . $year . $separator . $month . ')$%';
$regex['y'] = '%^(' . $fourDigitYear . ')$%';

次にymdの場合ですが、以下の正規表現になってます。

 $regex['ymd'] = '%^(?:(?:(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))' .
 $separator . '(?:0?2\\1(?:29)))|(?:(?:(?:1[6-9]|[2-9]\\d)?\\d{2})' .
 $separator . '(?:(?:(?:0?[13578]|1[02])\\2(?:31))|(?:(?:0?[1,3-9]|1[0-2])\\2(29|30))|(?:(?:0?[1-9])|(?:1[0-2]))\\2(?:0?[1-9]|1\\d|2[0-8]))))$%';

1600年から2999年までの日付しか対応していないようですね。
きっかけの1599年の日付はNGで1600年がOKなのはここの正規表現の影響だということがわかりました。
何故こうなっているのか理由まではわからなかったのですが、今のメジャーな暦のグレゴリオ暦が制定されたのが1582年らしいのでそれと関係してるのではないでしょうか。
うるう年のチェックなども入っているためそう考えてます。
もしご存知の方がいらっしゃったらコメントでこっそり教えていただければと思います。

まとめ

結果としては今回使用するのは生年月日なので1900年以前はエラーにするなど制限をすれば問題ないかなと思いました。
歴史系のコンテンツを扱うとかでどうしても1599年以前もバリデーションを通るようにしたい場合は正規表現を書き換えたり、独自に関数を作って対応していきましょう。

ということで大〇ドラマや戦国B△S△R△など歴史超大作のお仕事お待ちしております。

  • このエントリーをはてなブックマークに追加

この記事を読んだ人にオススメ