サンプル・プログラムの実行例
- 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.4 | 2024/07/27 | getDayToday()の取得パターンを変更した |
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(): 脚注パターン追加 |
解説:今日は何の日を取得
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: $pat1 = "/<h2\s+id\=\"できごと\"/iu"; //ver.2.1.4
278: $pat2 = "/<h2\s+id\=\"誕生日\"/iu"; //ver.2.1.4
279: $pat3 = "/<h2\s+id\=\"忌日\"/iu"; //ver.2.1.4
280: $pat5 = "/<li>[^\>]+([0-9]+(年|世紀).+)<\/li>/iu"; //年 - 内容 ver.2.11
281: $pat6 = "/([^0-9]*)([0-9]+)年(\s*[\((].[^\))]+[)\)].)?[ \-]+(.+)/iu"; //年 - イベント Ver.1.32
282: $pat7 = "/([^0-9]*)([0-9]+)年(\s*[\((].[^\))]+[)\)].)?[ \-]+([^、]+)、?([^(\(]+)*([^0-9]*)([0-9]*)/iu"; //年 - 名前、職業(生没年) Ver.1.43
283: $pat8 = "/<span[\s\S]+id=\"フィクションのできごと\">/iu"; //ver.1.5
284:
285: $url = getURL_Wikipedia($month, $day);
286:
287: $infp = @fopen($url, 'r');
288: if ($infp == FALSE) {
289: $errmsg = 'Wikipediaにアクセスできません.';
290: return FALSE;
291: }
292:
293: $mode = '';
294: $cnt = 1;
295: while (! feof($infp)) {
296: $str = fgets($infp);
297: if (preg_match($pat1, $str) == 1) {
298: $mode = 'event';
299: } else if (preg_match($pat8, $str) == 1) { //Ver.1.5
300: $mode = 'fiction'; //Ver.1.5
301: } else if (preg_match($pat2, $str) == 1) {
302: $mode = 'birth';
303: } else if (preg_match($pat3, $str) == 1) {
304: $mode = 'death';
305: } else if ($mode != '' && preg_match($pat5, $str, $arr) > 0) {
306: $str = strip_tags(trim($str));
307: if ($mode == 'event' && preg_match($pat6, $str, $arr) == 1) {
308: $items[$mode][$cnt]['year'] = ($arr[1] == '') ? (int)$arr[2] : 0 - $arr[2];
309: $items[$mode][$cnt]['era'] = $arr[3];
310: $items[$mode][$cnt]['event'] = $lfn ? $arr[4] : eliminate_footnote($arr[4]);
311: $cnt++;
312: } else if (preg_match($pat7, $str, $arr) == 1) {
313: $items[$mode][$cnt]['year'] = ($arr[1] == '') ? (int)$arr[2] : 0 - $arr[2];
314: $items[$mode][$cnt]['era'] = $arr[3];
315: $items[$mode][$cnt]['name'] = $lfn ? $arr[4] : eliminate_footnote($arr[4]);
316: $items[$mode][$cnt]['profile'] = $lfn ? $arr[5] : eliminate_footnote($arr[5]);
317: if (preg_match('/紀元前/u', $arr[6]) > 0) { //Ver.2.11
318: $items[$mode][$cnt]['plus'] = 0 - $arr[7];
319: } else if (preg_match('/^\([\*\+]./', $arr[6]) > 0) { //Ver.1.43
320: $items[$mode][$cnt]['plus'] = $arr[7];
321: } else {
322: $items[$mode][$cnt]['plus'] = '';
323: }
324: $cnt++;
325: }
326: }
327: }
328: fclose($infp);
329:
330: if ($cnt == 0) {
331: $errmsg = '記念日が見つかりません.';
332: }
333:
334: return $cnt;
335: }
これらを正規表現 $pat1 ~$pat7 を使ってスクレイピングする関数が getDayToday である。
Wikipediaのイベントや生没記述には揺れが大きく、これに対応するパターン $pat6 および $pat7 は、かなり複雑になっている。スクレイピングに成功すれば、配列要素として次のような値が入る。
要素 | 内容 |
---|---|
1 | 先頭文字 |
2 | 西暦年 |
3 | 和暦など(西暦年の後に括弧書きがあれば) |
4 | イベント、名前など |
5 | プロファイル(生没の場合のみ) |
6 | 生年または没年の前の記号 |
7 | 生年または没年(生没の場合のみ) |
解説:今日は何の日をソート
417: /**
418: * 今日は何の日 年号順にソート
419: * @param array $sour1 ソート前配列(Wikipedia)
420: * @param array $sour2 ソート前配列(日本記念日協会)
421: * @param array $dest ソード後配列
422: * @return int 情報件数
423: */
424: function sortDayToday($sour1, $sour2, &$dest) {
425: $cnt = 0;
426:
427: //できごと
428: foreach ($sour1['event'] as $item) {
429: $dest[$cnt]['year'] = $item['year'];
430: $dest[$cnt]['event'] = $item['event'];
431: $dest[$cnt]['era'] = $item['era'];
432: $cnt++;
433: }
434: //誕生日
435: foreach ($sour1['birth'] as $item) {
436: $event = "{$item['name']}({$item['profile']})誕生";
437: if ($item['plus'] != '') $event .= "(~{$item['plus']}年)";
438: $dest[$cnt]['year'] = $item['year'];
439: $dest[$cnt]['event'] = $event;
440: $dest[$cnt]['era'] = $item['era'];
441: $cnt++;
442: }
443: //命日
444: foreach ($sour1['death'] as $item) {
445: $event = "{$item['name']}({$item['profile']})死去";
446: if ($item['plus'] != '') $event .= "({$item['plus']}年~{$item['year']}年)";
447: $dest[$cnt]['year'] = $item['year'];
448: $dest[$cnt]['event'] = $event;
449: $dest[$cnt]['era'] = $item['era'];
450: $cnt++;
451: }
452: //記念日
453: foreach ($sour2 as $item) {
454: $dest[$cnt]['year'] = KINENBI_YEAR;
455: $dest[$cnt]['event'] = $item['title'];
456: $dest[$cnt]['era'] = '';
457: $dest[$cnt]['url'] = $item['url'];
458: $cnt++;
459: }
460:
461: //ソート
462: usort($dest, function ($a, $b) {
463: return (int)($a['year'] - $b['year']);
464: });
465:
466: return $cnt;
467: }
組み込み関数 usort を利用し、年号順に配列をソートしている。
解説:指定年月日から何日か離れた年月日
643: $month = getParam('month', FALSE, 0);
644: if ($month == 0) $month = date('n');
645: $day = getParam('day', FALSE, 0);
646: if ($day == 0) $day = date('j');
647: $year = getParam('year', FALSE, 0);
648: if ($year == 0) $year = date('Y');
649: $diff = getInteger('diff', -100, +100, 0);
650: list($year, $montjh, $day) = diffymd($year, $month, $day, $diff);
651: $era = getParam('era', FALSE, 1);
652: $era = ($era == 1) ? FALSE : TRUE;
653: $lfn = getParam('lfn', FALSE, 1); //Ver.1.43
654: $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 を呼び出すようにしてある。
組み込み関数 strtotime を使えば簡便に書けるのだが、この関数は32ビット環境ではUNIXタイムスタンプで1901年2月13日から2038年1月19日までしか計算できないため、あえてユーザー関数を用意した。
なお、コマンドラインから "DayToday.php?diff=+2" のように指定することで $diff を直接指定できる。
また、メイン・プログラムで $diff = +1 のように書き換えれば、「明日は何の日」を求めるプログラムに変身する。
活用例
参考サイト
- PHPで記念日を表示する:ぱふぅ家のホームページ
- C++ で今日は何の日か調べる:ぱふぅ家のホームページ
- 一般社団法人 日本記念日協会
これをPHPでスクレイピングすることで、今日は何の日か表示するプログラムを作成する。
さらに、「PHPで記念日を表示する」で作ったプログラムを取り込み、その日の記念日も表示できるようにする。
(2024年7月27日)Wikipediaのフォーマットが変わったことを受け、getDayToday()関数の取得パターンを変更した。