PHPで元号年月日入力(プルダウン方式)

(1/1)
PHP でプルダウンメニューを使って元号(明治、大正‥‥)年月日を入力するフォームを作る。元号の切り替わり(例:昭和 64 年 1 月 7 日の翌日は平成元年 1 月 8 日)に注意し、存在しない年月日は入力できないようにする。また、日数は大の月と小の月によって変わるようにし、とくに閏年の 2 月の入力に気をつける。

(2021 年 5 月 10 日)元号変更年における月・日の選択動作改善
(2021 年 4 月 24 日)PHP8 対応,リファラ・チェック改良

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

PHPで元号年月日入力

目次

サンプル・プログラムのダウンロード

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

解説:元号の開始と終了

0037: //元号=西暦一覧
0038: $eraTable = array(
0039:     '明治'   => array('from'=>'18680125', 'to'=>'19120729'),
0040:     '大正'   => array('from'=>'19120730', 'to'=>'19261224'),
0041:     '昭和'   => array('from'=>'19261225', 'to'=>'19890107'),
0042:     '平成'   => array('from'=>'19890108', 'to'=>'20190430'),
0043:     '令和'   => array('from'=>'20190501', 'to'=>'29991231')
0044: );

プログラムの部品は、いままで作ってきた「PHP で日本語対応 date を作る」「PHP で年月日を入力」の寄せ集めである。

あらたに作るのは、元号の切り替わり(例:昭和 64 年 1 月 7 日の翌日は平成元年 1 月 8 日)に注意して年月日のプルダウンを制御する部分である。そのために、あらかじめ西暦と元号の関係表をグローバル変数 $eraTable に用意しておく。

解説:西暦年月日を元号に変換

0180: /**
0181:  * 西暦年月日を元号に変換
0182:  * @param int $year  西暦年
0183:  * @param int $month 月
0184:  * @param int $day   日
0185:  * @return array era=元号, year=元号年, month=月, day=日
0186: */
0187: function ad2era($year$month$day) {
0188:     global $eraTable;
0189: 
0190:     $res = array('era'=>'', 'year'=>$year, 'month'=>$month, 'day'=>$day);
0191:     $yyyymmdd = sprintf('%04d%02d%02d', $year$month$day);
0192: 
0193:     foreach ($eraTable as $era=>$val) {
0194:         if ($yyyymmdd <= $val['to']) {
0195:             $res['era'] = $era;
0196:             $y = (int)substr($eraTable[$eraset['era']]['from'], 0, 4);
0197:             $res['year'] = $year - $y + 1;
0198:             break;
0199:         }
0200:     }
0201: 
0202:     return $res;
0203: }

ユーザー関数 ad2era は、西暦年月日を引数とし、グローバル変数 $eraTable を参照し、対応する元号・元号年月日を返す。

解説:元号を西暦年月日に変換

0205: /**
0206:  * 元号を西暦年月日に変換
0207:  * @param array eraset  元号年月日 era=元号, year=元号年, month=月, day=日
0208:  * @return array year=西暦年, month=月, day=日
0209: */
0210: function era2ad($eraset) {
0211:     global $eraTable;
0212: 
0213:     $res = array('year'=>$eraset['year'], 'month'=>$eraset['month'], 'day'=>$eraset['day']);
0214:     $yyyymmdd = $eraTable[$eraset['era']]['from'];
0215:     $year = (int)substr($yyyymmdd, 0, 4);
0216:     $res['year']  = $eraset['year'] + $year - 1;
0217: 
0218:     return $res;
0219: }

ユーザー関数 era2ad は、ad2era の逆関数である。

解説:指定した元号の年数・月数・日数

0221: /**
0222:  * 指定した元号の年数・月数・日数を返す
0223:  * @param array $eraset 元号年月日 era=元号, year=元号年, month=月, day=日
0224:  * @return array yy=年数, mm=月数, dd=日数
0225: */
0226: function getYymmddInEra($eraset) {
0227:     global $eraTable;
0228: 
0229: 
0230:     $res = era2ad($eraset);      //元号を西暦年月日に変換
0231:     $year = $res['year'];
0232: 
0233:     //年数
0234:     $y0 = (int)substr($eraTable[$eraset['era']]['from'], 0, 4);
0235:     $y1 = (int)substr($eraTable[$eraset['era']]['to'],   0, 4);
0236:     $yy = $y1 - $y0 + 1;
0237:     if ($eraset['year'] > $yy) {
0238:         $eraset['year'] = 1;
0239:         $res = era2ad($eraset);      //元号を西暦年月日に変換
0240:         $year = $res['year'];
0241:     }
0242: 
0243:     //月数
0244:     if ($year == $y0) {
0245:         $m0 = (int)substr($eraTable[$eraset['era']]['from'], 4, 2);
0246:         $m1 = 12;
0247:         if ($eraset['month'] < $m0)  $eraset['month'] = $m0;
0248:     } else if ($year == $y1) {
0249:         $m0 = 1;
0250:         $m1 = (int)substr($eraTable[$eraset['era']]['to'], 4, 2);
0251:         if ($eraset['month'] > $m1)  $eraset['month'] = $m1;
0252:     } else {
0253:         $m0 = 1;
0254:         $m1 = 12;
0255:     }
0256: 
0257:     //日数
0258:     $ym0 = substr($eraTable[$eraset['era']]['from'], 0, 6);
0259:     $ym1 = substr($eraTable[$eraset['era']]['to'],   0, 6);
0260:     $yymm = sprintf('%04d%02d', $year$eraset['month']);
0261: 
0262:     static $days = array(0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
0263:     $days[2] = isleap($year) ? 29 : 28;       //閏年の判定
0264:     if ($yymm == $ym0) {
0265:         $d0 = (int)substr($eraTable[$eraset['era']]['from'], 6, 2);
0266:         $d1 = $days[$eraset['month']];
0267:         if ($eraset['day'] < $d0)  $eraset['day'] = $d0;
0268:     } else if ($yymm == $ym1) {
0269:         $d0 = 1;
0270:         $d1 = (int)substr($eraTable[$eraset['era']]['to'], 6, 2);
0271:         if ($eraset['day'] > $d1)  $eraset['day'] = $d1;
0272:     } else {
0273:         $d0 = 1;
0274:         $d1 = $days[$eraset['month']];
0275:     }
0276: 
0277:     return array('yy'=>array('from'=>1, 'to'=>$yy), 'mm'=>array('from'=>$m0, 'to'=>$m1), 'dd'=>array('from'=>$d0, 'to'=>$d1));
0278: }

たとえば「昭和 64 年」を選択した場合、月は「1 月」だけ、日は「1~7 日」までしか選択できないようにする。
そこで、引数として元号年月日を渡すと、年・月・日の範囲を配列で返すユーザー関数が getYymmddInEra である。
同時に、閏年のときは 2 月 29 日になるようにする。

解説:元号年月日をプルダウンメニューにする

0360: //プルダウンメニュー
0361:     $outstr = '';
0362:     $name = 'exec';
0363: //元号セレクタ
0364: $outstr .=<<< EOT
0365: <select name="era" id="era" onChange="submit();">
0366: 
0367: EOT;
0368:     $eraset2['era'] = '';
0369:     foreach ($eraTable as $era=>$val) {
0370:         if ($era == $eraset['era']) {
0371:             $selected = 'selected';
0372:             $eraset2['era'] = $era;
0373:         } else {
0374:             $selected = '';
0375:         }
0376: $outstr .=<<< EOT
0377: <option value="{$era}" {$selected}>{$era}</option>
0378: 
0379: EOT;
0380:     }
0381: $outstr .=<<< EOT
0382: </select>
0383: 
0384: EOT;
0385: 
0386: //年号セレクタ
0387: $outstr .=<<< EOT
0388: <select name="year" id="year" onChange="submit();">
0389: 
0390: EOT;
0391:     $eraset2['year'] = 0;
0392:     for ($yy = $res['yy']['from']; $yy <= $res['yy']['to']; $yy++) {
0393:         if ($yy == $eraset['year']) {
0394:             $selected = 'selected';
0395:             $eraset2['year'] = $yy;
0396:         } else {
0397:             $selected = '';
0398:         }
0399:         $yn = ($yy == 1) ? '' : $yy;
0400: $outstr .=<<< EOT
0401: <option value="{$yy}" {$selected}>{$yn}</option>
0402: 
0403: EOT;
0404:     }
0405: $outstr .=<<< EOT
0406: </select>年
0407: 
0408: EOT;
0409: 
0410: //月セレクタ
0411: $outstr .=<<< EOT
0412: <select name="month" id="month" onChange="submit();">
0413: 
0414: EOT;
0415:     $eraset2['month'] = 1;
0416:     for ($mm = $res['mm']['from']; $mm <= $res['mm']['to']; $mm++) {
0417:         if ($mm == $eraset['month']) {
0418:             $selected = 'selected';
0419:             $eraset2['month'] = $mm;
0420:         } else {
0421:             $selected = '';
0422:         }
0423: $outstr .=<<< EOT
0424: <option value="{$mm}" {$selected}>{$mm}</option>
0425: 
0426: EOT;
0427:     }
0428: $outstr .=<<< EOT
0429: </select>月
0430: 
0431: EOT;
0432: 
0433: //日セレクタ
0434: $outstr .=<<< EOT
0435: <select name="day" id="day" onChange="submit();">
0436: 
0437: EOT;
0438:     $eraset2['day'] = 1;
0439:     for ($dd = $res['dd']['from']; $dd <= $res['dd']['to']; $dd++) {
0440:         if ($dd == $eraset['day']) {
0441:             $selected = 'selected';
0442:             $eraset2['day'] = $dd;
0443:         } else {
0444:             $selected = '';
0445:         }
0446: $outstr .=<<< EOT
0447: <option value="{$dd}" {$selected}>{$dd}</option>
0448: 
0449: EOT;
0450:     }
0451: 
0452:     $ad = era2ad($eraset2);      //元号を西暦年月日に変換
0453: $outstr .=<<< EOT
0454: </select>日
0455: 
0456: <p>西暦 {$ad['year']} 年 {$ad['month']} 月 {$ad['day']} 日</p>
0457: 
0458: EOT;
0459: 
0460: $body =<<< EOT

プルダウンメニューは、元号(era)、年(year)、月(month)、日(date)を別々のタグにする。

このプルダウンメニューが変化したら、JavaScript の onChange イベントで submit 駆動し、form 内容を送信するようにしている。これは元号(era)、年(year)、月(month)の場合も同様で、これを行うことで、元号・年・月・日を変更した際に再計算を行う。

ただし、いちいちサーバサイドで計算させることはサーバに負荷がかかるので、この処理は、本来はクライアントサイドで JavaScript で行わせるのが良いだろう。JavaScript による処理については、「元号による年月日セレクタ」で紹介している。

参考サイト

(この項おわり)
header