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

サンプル・プログラム
プログラムの方針
差分は、年、月、日の3種類とし、同時に3つ指定できるものとする。正数を指定すると加算(未来日)、負数を指定すると減算(過去日)になるようにする。
桁合わせのために "2018/02/03" のように数字の頭にゼロを付ける(ゼロ・サフィックス)されたデータがあるが、たとえばソースが "2018/11/24" のようなデータはゼロ・サフィックスを行うべきかどうか自動判断できない。そこで、ゼロ・サフィックスを手動指定できるようチェックボックスを設ける。
解説:差分計算(日)
209: /**
210: * 指定年月日から$diff日だけ離れた年月日を取得する
211: * @param int $year 西暦年
212: * @param int $month 月
213: * @param int $day 日
214: * @param int $diff 差分日数
215: * @return array(年,月,日)
216: */
217: function diffymd($year, $month, $day, $diff) {
218: $dm = getDaysInMonth($year, $month);
219:
220: $day += $diff;
221: //月の繰り下がり
222: while ($day < 1) {
223: $month--;
224: if ($month < 1) $year--;
225: $dm = getDaysInMonth($year, $month);
226: $day += $dm;
227: }
228: //月の繰り上がり
229: while ($day > $dm) {
230: $dm = getDaysInMonth($year, $month);
231: $day -= $dm;
232: $month++;
233: if ($month > 12) {
234: $year++;
235: $month = 1;
236: }
237: }
238:
239: return array($year, $month, $day);
240: }
加算結果が1未満となった場合、月の繰り下がりが発生する。$day が1以上になるまで、while ループを回して繰り下がりを処理し続ける。

月の日数を取得するユーザー関数は getDaysInMonth である。うるう年の2月は29が返るようにしてある。
解説:差分計算(年・月)
242: /**
243: * 指定年月日から$yy年$mm月だけ離れた年月日を取得する
244: * @param int $year 西暦年
245: * @param int $month 月
246: * @param int $day 日
247: * @param int $yy 差分年数
248: * @param int $mm 差分月数
249: * @return array(年,月,日)
250: */
251: function diffymd_ym($year, $month, $day, $yy, $mm) {
252: $year += $yy;
253: $month += $mm;
254:
255: //年の繰り下がり
256: while ($month < 1) {
257: $year--;
258: $month += 12;
259: }
260: //年の繰り上がり
261: while ($month > 12) {
262: $year++;
263: $month -= 12;
264: }
265:
266: return array($year, $month, $day);
267: }
解説:テキスト中の日付をN日後に置換
269: /**
270: * テキスト中の日付をN日後に置換
271: * @param string $sour オリジナル・テキスト
272: * @return string 変換後テキスト
273: */
274: function dayafter($sour) {
275: //検索パターン
276: $pat = '/(([0-9]+)([ 年\/\-]+))(([0-9]+)([ 月\/\-]+))(([0-9]+)([ 日])*)/iums';
277:
278: return preg_replace_callback($pat,
279: //置換関数
280: function ($mat) {
281: global $Diff;
282: if (! isset($mat[9])) $mat[9] = '';
283: list($year, $month, $day) = diffymd((int)$mat[2], (int)$mat[5], (int)$mat[8], $Diff['day']);
284: list($year, $month, $day) = diffymd_ym($year, $month, $day, $Diff['year'], $Diff['month']);
285: //ゼロ・プレフィックス
286: if ($Diff['prefix']) {
287: $res = ($mat[2] != '') ? sprintf('%04d%s', $year, $mat[3]) : '';
288: $res .= sprintf('%02d%s%02d%s', $month, $mat[6], $day, $mat[9]);
289: //なし
290: } else {
291: $res = ($mat[2] != '') ? sprintf('%d%s', $year, $mat[3]) : '';
292: $res .= sprintf('%d%s%d%s', $month, $mat[6], $day, $mat[9]);
293: }
294: return $res;
295: }, $sour);
296: }
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日までしか計算できないという制約があるため、今回、あえてユーザー関数として自作した。