サンプル・プログラムの実行例
サンプル・プログラム
プログラムの方針
差分は、年、月、日の3種類とし、同時に3つ指定できるものとする。正数を指定すると加算(未来日)、負数を指定すると減算(過去日)になるようにする。
桁合わせのために "2018/02/03" のように数字の頭にゼロを付ける(ゼロ・サフィックス)されたデータがあるが、たとえばソースが "2018/11/24" のようなデータはゼロ・サフィックスを行うべきかどうか自動判断できない。そこで、ゼロ・サフィックスを手動指定できるようチェックボックスを設ける。
解説:差分計算(日)
0209: /**
0210: * 指定年月日から$diff日だけ離れた年月日を取得する
0211: * @param int $year 西暦年
0212: * @param int $month月
0213: * @param int $day 日
0214: * @param int $diff 差分日数
0215: * @return array(年,月,日)
0216: */
0217: function diffymd($year, $month, $day, $diff) {
0218: $dm = getDaysInMonth($year, $month);
0219:
0220: $day += $diff;
0221: //月の繰り下がり
0222: while ($day < 1) {
0223: $month--;
0224: if ($month < 1) $year--;
0225: $dm = getDaysInMonth($year, $month);
0226: $day += $dm;
0227: }
0228: //月の繰り上がり
0229: while ($day > $dm) {
0230: $dm = getDaysInMonth($year, $month);
0231: $day -= $dm;
0232: $month++;
0233: if ($month > 12) {
0234: $year++;
0235: $month = 1;
0236: }
0237: }
0238:
0239: return array($year, $month, $day);
0240: }
加算結果が1未満となった場合、月の繰り下がりが発生する。$day が1以上になるまで、while ループを回して繰り下がりを処理し続ける。
月の日数を取得するユーザー関数は getDaysInMonth である。うるう年の2月は29が返るようにしてある。
解説:差分計算(年・月)
0242: /**
0243: * 指定年月日から$yy年$mm月だけ離れた年月日を取得する
0244: * @param int $year 西暦年
0245: * @param int $month月
0246: * @param int $day 日
0247: * @param int $yy 差分年数
0248: * @param int $mm 差分月数
0249: * @return array(年,月,日)
0250: */
0251: function diffymd_ym($year, $month, $day, $yy, $mm) {
0252: $year += $yy;
0253: $month += $mm;
0254:
0255: //年の繰り下がり
0256: while ($month < 1) {
0257: $year--;
0258: $month += 12;
0259: }
0260: //年の繰り上がり
0261: while ($month > 12) {
0262: $year++;
0263: $month -= 12;
0264: }
0265:
0266: return array($year, $month, $day);
0267: }
解説:テキスト中の日付をN日後に置換
0269: /**
0270: * テキスト中の日付をN日後に置換
0271: * @param string $sour オリジナル・テキスト
0272: * @return string変換後テキスト
0273: */
0274: function dayafter($sour) {
0275: //検索パターン
0276: $pat = '/(([0-9]+)([ 年\/\-]+))(([0-9]+)([ 月\/\-]+))(([0-9]+)([ 日])*)/iums';
0277:
0278: return preg_replace_callback($pat,
0279: //置換関数
0280: function ($mat) {
0281: global $Diff;
0282: if (! isset($mat[9])) $mat[9] = '';
0283: list($year, $month, $day) = diffymd((int)$mat[2], (int)$mat[5], (int)$mat[8], $Diff['day']);
0284: list($year, $month, $day) = diffymd_ym($year, $month, $day, $Diff['year'], $Diff['month']);
0285: //ゼロ・プレフィックス
0286: if ($Diff['prefix']) {
0287: $res = ($mat[2] != '') ? sprintf('%04d%s', $year, $mat[3]) : '';
0288: $res .= sprintf('%02d%s%02d%s', $month, $mat[6], $day, $mat[9]);
0289: //なし
0290: } else {
0291: $res = ($mat[2] != '') ? sprintf('%d%s', $year, $mat[3]) : '';
0292: $res .= sprintf('%d%s%d%s', $month, $mat[6], $day, $mat[9]);
0293: }
0294: return $res;
0295: }, $sour);
0296: }
preg_replace_callback を利用して、日付を表す部分文字列にマッチングさせ、コールバック関数の中で切り出した年・月・日に対して差分計算する。
日付にマッチさせる正規表現は次の通り。
(([0-9]+)([ 年\/\-]+))?(([0-9]+)([ 月\/\-]+))(([0-9]+)([ 日]*))
年月日のセパレータは「年・月・日・/・-」を想定している。数字との間に空白文字が入ることも許容している。また、年は必ずしも必要ないが、年月は必須である。
マッチングに成功すると、配列要素として次のような値が入り、コールバック関数に渡される。
要素 | 内容 |
---|---|
1 | 年+セパレータ(空文字あり) |
2 | 年(空文字あり) |
3 | 年のセパレータ(空文字あり) |
4 | 月+セパレータ |
5 | 月 |
6 | 月のセパレータ |
7 | 日+セパレータ |
8 | 日 |
9 | 日のセパレータ(空文字あり) |
なお、PHPには、 strtotime や date_diff という関数で差分日付の計算が可能だが、1901年(明治34年)12月13日から2038年(令和20年)1月19日までしか計算できないという制約があるため、今回、あえてユーザー関数として自作した。