それは正しい日付ですか?
さて早速なのですが、以下のようにフォームに入力があったとします。お誕生日が2月29日です。閏年にしかない日付ですが、2000年は閏年だったでしょうか?確か4で割り切れる年は閏年でしたね。でも100で割り切れる年は閏年でなかった気が...うーん、そうすると、これは入力ミスでしょうか?
正解は、正しい入力です。400で割り切れる年は閏年なので、2000年の2月29日は存在しました。
日付の入力チェックというのはこのように、単純な形式チェックだけで済まない上、間違った日付はシステム上のエラーを引き起こすことも多いですのでデータの入り口でチェックが必要です。
なお、入力チェックの事を「バリデーション」とも言います。
入り口でのバリデーション方法
Web上の入力フォームの世界では入力ミスを防ぐために数字のプルダウンメニューで日付を入力させることもあります。しかしながら、年と月に合わせて日の選択リストを28から31日までのどれかに切り替えているかと言うとそこまではやっていないWebサイトの方が多いようです。Perlでの日付チェック方法
ということで、確実なバリデーションのためには、Web上のフォームやその他の方法で受け付けた日付情報をチェックするプログラムが必要になりますが、Perl の場合は標準モジュール Time::Local を使って簡単にこのチェックができます。サンプルコードは以下の通りです。
use Time::Local; #日付が存在するかチェックする #引数: 年、月 (1-12)、日 #戻り値:フラグ (1=OK、0=NG) sub if_date_exists{ my ($year, $month, $day) = @_; $year and $month and $day and $year =~ m/^\d+$/ and $month =~ m/^\d+$/ and $day =~ m/^\d+$/ or return 0 ; my $epoc = eval{timelocal(0,0,0,$day, $month-1, $year)}; defined $epoc and return 1 or return 0 ; }Time::Local に含まれる timelocal という関数 (上記太字部分) は日付からUNIX時間を算出してくれます。ただし、存在しない日付情報を引数として与えるとエラーで計算ができません。この性質を利用して、計算結果があれば正しい日付情報だった、と判別しているのです。
なお、このように「エラーが出る」事を利用した処理を行う場合、プログラム全体が停止してしまわないようにエラーが出る可能性のある部分を「eval{ }」で囲うのがPerl プログラミング上のテクニックです。他のプログラミング言語の try-catch に相当します。
チェックサブルーチン利用方法
フォームなどの入力チェックに利用する場合は先に入力値の半角変換を行ってからこのチェックを行います。この時付け足す処理は、例えば以下の通りです。
$value = &zen2han($value); # formutil.pl 参照 unless (&is_date_format($value)){ $error_msg = "日付は半角の yyyy-mm-dd 形式で入力して下さい。"; #ここにエラー処理を書く } #日付の形式チェック #yyyy-mm-dd 形式のみを受け入れる #引数:文字列 #戻り値:フラグ (1=OK、0=NG) sub is_date_format{ my $str = shift; defined $str and length $str or return 0; return &if_date_exists(split(/-/, $str)); #先ほど作成したサブルーチン }