サンプル・プログラムの実行例
![PHPで今日は何の日か調べる](php05-16-01.png)
- month, day = 月,日の指定
- (例)DayToday.php?month=8&day=2 (8月2日を指定する)
- diff = 今日を基準とした差分日数指定(-100~+100)
- (例)DayToday.php?diff=+2 (明後日を指定する)
- era = 西暦年の下段に和暦などを表示
- (例)DayToday.php?era
- lfn = 脚注を残す
- (例)DayToday.php?lfn
- anniv = 記念日を表示する
- (例)DayToday.php?anniv
サンプル・プログラム
DayToday.php | サンプル・プログラム本体。 |
バージョン | 更新日 | 内容 |
---|---|---|
2.1.3 | 2023/01/05 | getDayToday()の取得パターンを変更した |
2.12 | 2022/10/15 | 人物の紀元前パターンに対応 |
2.11 | 2022/10/15 | 紀元前のHTMLパターン変更に対応 |
2.1 | 2021/10/10 | eliminate_footnote(): 脚注パターン追加 |
2.0 | 2021/01/26 | annivオプションを追加(記念日を表示) |
解説:今日は何の日を取得
254: /**
255: * Wikipedia URL取得
256: * @param int $month, $day 月,日
257: * @return string URL
258: */
259: function getURL_Wikipedia($month, $day) {
260: $url = 'http://ja.wikipedia.org/wiki/' . urlencode(sprintf('%d月%d日', $month, $day));
261:
262: return $url;
263: }
265: /**
266: * 今日は何の日 取得
267: * @param int $month, $day 月,日
268: * @param array $items 情報格納配列
269: * @param string $errmsg エラーメッセージ格納用
270: * @param bool $lfn 脚注残す(省略時=FALSE)
271: * @return int 情報件数
272: */
273: function getDayToday($month, $day, &$items, &$errmsg, $lfn=FALSE) {
274: $pat1 = "/<span[\s\S]+id=\"できごと\"/iu"; //ver.2.1.3
275: $pat2 = "/<span[\s\S]+id=\"誕生日\"/iu"; //ver.2.1.3
276: $pat3 = "/<span[\s\S]+id=\"忌日\"/iu"; //ver.2.1.3
277: $pat5 = "/<li>[^\>]+([0-9]+(年|世紀).+)<\/li>/iu"; //年 - 内容 ver.2.11
278: $pat6 = "/([^0-9]*)([0-9]+)年(\s*[\((].[^\))]+[)\)].)?[ \-]+(.+)/iu"; //年 - イベント Ver.1.32
279: $pat7 = "/([^0-9]*)([0-9]+)年(\s*[\((].[^\))]+[)\)].)?[ \-]+([^、]+)、?([^(\(]+)*([^0-9]*)([0-9]*)/iu"; //年 - 名前、職業(生没年) Ver.1.43
280: $pat8 = "/<span[\s\S]+id=\"フィクションのできごと\">/iu"; //ver.1.5
281:
282: $url = getURL_Wikipedia($month, $day);
283:
284: $infp = @fopen($url, 'r');
285: if ($infp == FALSE) {
286: $errmsg = 'Wikipediaにアクセスできません.';
287: return FALSE;
288: }
289:
290: $mode = '';
291: $cnt = 1;
292: while (! feof($infp)) {
293: $str = fgets($infp);
294: if (preg_match($pat1, $str) == 1) {
295: $mode = 'event';
296: } else if (preg_match($pat8, $str) == 1) { //Ver.1.5
297: $mode = 'fiction'; //Ver.1.5
298: } else if (preg_match($pat2, $str) == 1) {
299: $mode = 'birth';
300: } else if (preg_match($pat3, $str) == 1) {
301: $mode = 'death';
302: } else if ($mode != '' && preg_match($pat5, $str, $arr) > 0) {
303: $str = strip_tags(trim($str));
304: if ($mode == 'event' && preg_match($pat6, $str, $arr) == 1) {
305: $items[$mode][$cnt]['year'] = ($arr[1] == '') ? (int)$arr[2] : 0 - $arr[2];
306: $items[$mode][$cnt]['era'] = $arr[3];
307: $items[$mode][$cnt]['event'] = $lfn ? $arr[4] : eliminate_footnote($arr[4]);
308: $cnt++;
309: } else if (preg_match($pat7, $str, $arr) == 1) {
310: $items[$mode][$cnt]['year'] = ($arr[1] == '') ? (int)$arr[2] : 0 - $arr[2];
311: $items[$mode][$cnt]['era'] = $arr[3];
312: $items[$mode][$cnt]['name'] = $lfn ? $arr[4] : eliminate_footnote($arr[4]);
313: $items[$mode][$cnt]['profile'] = $lfn ? $arr[5] : eliminate_footnote($arr[5]);
314: if (preg_match('/紀元前/u', $arr[6]) > 0) { //Ver.2.11
315: $items[$mode][$cnt]['plus'] = 0 - $arr[7];
316: } else if (preg_match('/^\([\*\+]./', $arr[6]) > 0) { //Ver.1.43
317: $items[$mode][$cnt]['plus'] = $arr[7];
318: } else {
319: $items[$mode][$cnt]['plus'] = '';
320: }
321: $cnt++;
322: }
323: }
324: }
325: fclose($infp);
326:
327: if ($cnt == 0) {
328: $errmsg = '記念日が見つかりません.';
329: }
330:
331: return $cnt;
332: }
これらを正規表現 $pat1 ~$pat7 を使ってスクレイピングする関数が getDayToday である。
![](../../../common/space.gif)
Wikipediaのイベントや生没記述には揺れが大きく、これに対応するパターン $pat6 および $pat7 は、かなり複雑になっている。スクレイピングに成功すれば、配列要素として次のような値が入る。
要素 | 内容 |
---|---|
1 | 先頭文字 |
2 | 西暦年 |
3 | 和暦など(西暦年の後に括弧書きがあれば) |
4 | イベント、名前など |
5 | プロファイル(生没の場合のみ) |
6 | 生年または没年の前の記号 |
7 | 生年または没年(生没の場合のみ) |
解説:今日は何の日をソート
414: /**
415: * 今日は何の日 年号順にソート
416: * @param array $sour1 ソート前配列(Wikipedia)
417: * @param array $sour2 ソート前配列(日本記念日協会)
418: * @param array $dest ソード後配列
419: * @return int 情報件数
420: */
421: function sortDayToday($sour1, $sour2, &$dest) {
422: $cnt = 0;
423:
424: //できごと
425: foreach ($sour1['event'] as $item) {
426: $dest[$cnt]['year'] = $item['year'];
427: $dest[$cnt]['event'] = $item['event'];
428: $dest[$cnt]['era'] = $item['era'];
429: $cnt++;
430: }
431: //誕生日
432: foreach ($sour1['birth'] as $item) {
433: $event = "{$item['name']}({$item['profile']})誕生";
434: if ($item['plus'] != '') $event .= "(~{$item['plus']}年)";
435: $dest[$cnt]['year'] = $item['year'];
436: $dest[$cnt]['event'] = $event;
437: $dest[$cnt]['era'] = $item['era'];
438: $cnt++;
439: }
440: //命日
441: foreach ($sour1['death'] as $item) {
442: $event = "{$item['name']}({$item['profile']})死去";
443: if ($item['plus'] != '') $event .= "({$item['plus']}年~{$item['year']}年)";
444: $dest[$cnt]['year'] = $item['year'];
445: $dest[$cnt]['event'] = $event;
446: $dest[$cnt]['era'] = $item['era'];
447: $cnt++;
448: }
449: //記念日
450: foreach ($sour2 as $item) {
451: $dest[$cnt]['year'] = KINENBI_YEAR;
452: $dest[$cnt]['event'] = $item['title'];
453: $dest[$cnt]['era'] = '';
454: $dest[$cnt]['url'] = $item['url'];
455: $cnt++;
456: }
457:
458: //ソート
459: usort($dest, function ($a, $b) {
460: return (int)($a['year'] - $b['year']);
461: });
462:
463: return $cnt;
464: }
組み込み関数 usort を利用し、年号順に配列をソートしている。
解説:指定年月日から何日か離れた年月日
640: $month = getParam('month', FALSE, 0);
641: if ($month == 0) $month = date('n');
642: $day = getParam('day', FALSE, 0);
643: if ($day == 0) $day = date('j');
644: $year = getParam('year', FALSE, 0);
645: if ($year == 0) $year = date('Y');
646: $diff = getInteger('diff', -100, +100, 0);
647: list($year, $montjh, $day) = diffymd($year, $month, $day, $diff);
648: $era = getParam('era', FALSE, 1);
649: $era = ($era == 1) ? FALSE : TRUE;
650: $lfn = getParam('lfn', FALSE, 1); //Ver.1.43
651: $lfn = ($lfn == 1) ? FALSE : TRUE; //Ver.1.43
191: /**
192: * 指定した年月の日数を返す
193: * @param int $year 年
194: * @param int $month 月
195: * @return int 日数/FALSE:引数の異常
196: */
197: function getDaysInMonth($year, $month) {
198: static $days = array(0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
199: if ($month < 1 || $month > 12) return FALSE;
200:
201: $dm = (($month == 2) && isLeapYear($year)) ? 29 : $days[$month];
202:
203: return $dm;
204: }
206: /**
207: * 指定年月日から$diff日だけ離れた年月日を取得する
208: * @param int $year 西暦年
209: * @param int $month 月
210: * @param int $day 日
211: * @param int $diff 差分日数
212: * @return array(年,月,日)
213: */
214: function diffymd($year, $month, $day, $diff) {
215: $dm = getDaysInMonth($year, $month);
216:
217: $day += $diff;
218: //月の繰り下がり
219: while ($day < 1) {
220: $month--;
221: if ($month < 1) $year--;
222: $dm = getDaysInMonth($year, $month);
223: $day += $dm;
224: }
225: //月の繰り上がり
226: while ($day > $dm) {
227: $dm = getDaysInMonth($year, $month);
228: $day -= $dm;
229: $month++;
230: if ($month > 12) $year++;
231: }
232:
233: return array($year, $month, $day);
234: }
月の大小が異なることから getDaysInMonth を呼び出し、うるう年の判定は isleap を呼び出すようにしてある。
![](../../../common/space.gif)
組み込み関数 strtotime を使えば簡便に書けるのだが、この関数は32ビット環境ではUNIXタイムスタンプで1901年2月13日から2038年1月19日までしか計算できないため、あえてユーザー関数を用意した。
![](../../../common/space.gif)
なお、コマンドラインから "DayToday.php?diff=+2" のように指定することで $diff を直接指定できる。
また、メイン・プログラムで $diff = +1 のように書き換えれば、「明日は何の日」を求めるプログラムに変身する。
活用例
参考サイト
- PHPで記念日を表示する:ぱふぅ家のホームページ
- C++ で今日は何の日か調べる:ぱふぅ家のホームページ
- 一般社団法人 日本記念日協会
これをPHPでスクレイピングすることで、今日は何の日か表示するプログラムを作成する。
さらに、「PHPで記念日を表示する」で作ったプログラムを取り込み、その日の記念日も表示できるようにする。