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

サンプル・プログラム
cosmicCalendar.php | サンプル・プログラム本体。 |
cosmicCalendar.xml | イベント・ファイル。 |
pahooCalendar.php | 暦計算クラス pahooCalendar。 暦計算クラスの使い方は「PHPで日出没・月出没・月齢・潮を計算」を参照。include_path が通ったディレクトリに配置すること。 |
バージョン | 更新日 | 内容 |
---|---|---|
1.5.0 | 2022/12/30 | 1月は時分まで表示するようにした |
1.42 | 2022/07/21 | bug-fix,イベント・ファイル更新 |
1.41 | 2021/05/12 | bug-fix |
1.4 | 2021/01/16 | PHP8対応 |
1.3 | 2020/01/03 | リファラチェック追加 |
イベント・ファイル
同梱したファイルには、宇宙の誕生から現在までの主要イベントを記入済みである。
このイベント・ファイルを編集すれば、日本史や家族史を使った宇宙カレンダー(365日カレンダーと呼んだ方がいいかもしれない)を表示することができる。
   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <!-- 年代記 -->
   3: <cosmicCalendar>
   4: <!-- キャプション -->
   5: <caption>
   6: <label>宇宙カレンダー</label>
   7: <description>宇宙誕生から現在までを1年に圧縮</description>
   8: </caption>
   9: <!-- 開始 -->
  10: <start>
  11: <label>宇宙誕生</label>
  12: <before unit="year">138E+8</before>
  13: <description></description>
  14: </start>
  15: <finish>
  16: <label>現在</label>
  17: <before unit="year">0</before>
  18: <description></description>
  19: </finish>
  20:
  21: <!-- 以下、イベント・データベース -->
  22: <event>
  23: <label>宇宙の晴れ上がり</label>
  24: <!-- 開始イベントからの年数を記載するときは after -->
  25: <after unit="year">30E+4</after>
  26: <description></description>
  27: </event>
  28: <event>
  29: <label>天の川銀河の誕生</label>
  30: <!-- 現在から××年前を記載するときは before -->
  31: <before unit="year">110E+8</before>
  32: <description></description>
  33: </event>
  34: <event>
  35: <label>太平洋戦争終結</label>
  36: <!-- 西暦記載するときは dt で yyyy/mm/dd hh:mm:ss形式 -->
  37: <dt>1945/8/15 00:00:00</dt>
  38: <description></description>
  39: </event>
  40: <event>
  41: <label>ハンムラビ王の即位</label>
  42: <!-- 紀元前はマイナス表記 -->
  43: <dt>-1792/1/1 00:00:00</dt>
  44: <description></description>
  45: </event>
  46: <event>
  47: <label>最初の恒星</label>
  48: <!-- -->
  49: <after unit="year">1.8E+8</after>
  50: <description>2018年3月1日に、米アリゾナ州立大などの国際研究チームが発表した。</description>
準備:外部クラスなど
そこで、クラスファイル "pahooCalendar.php" を require_once し、オブジェクトを生成する。
解説:イベント・ファイルを読み込み、ソートする
 276: /**
 277: * イベント・ファイルを読み込み、ソートする
 278: * @param string $fname イベント・ファイル名
 279: * @param array $caldb イベントを格納する配列
 280: * @param array $caption キャプションを格納する配列
 281: * @return bool TRUE/FALSE
 282: */
 283: function readDB($fname, &$caldb, &$caption) {
 284: $pcl = new pahooCalendar(); //pahooCalendarクラス
 285: $pcl->setLanguage('jp');
 286:
 287: $jd_end = $pcl->Gregorian2JD(date('Y') + 1, 1, 1, 0, 0, 0); //今年の最後
 288: $dd = 365 + date('L'); //1年の日数
 289: $res = FALSE;
 290:
 291: //イベント・ファイル読み込み
 292: if (file_exists($fname)) {
 293: $xml = simplexml_load_file($fname);
 294: //キャプション
 295: if (isset($xml->caption->label)) {
 296: $caption['label'] = isset($xml->caption->label) ? (string)$xml->caption->label : '宇宙カレンダー';
 297: $caption['description'] = isset($xml->caption->description) ? (string)$xml->caption->description : '宇宙誕生から現在までを1年に圧縮';
 298: }
 299: //開始
 300: if (isset($xml->start->before)) {
 301: $start = (double)$xml->start->before;
 302: $unit = ($dd * 24 * 60 * 60) / $start; //1秒当たり年数
 303: $cnt = 0;
 304: $caldb[$cnt]['label'] = (string)$xml->start->label;
 305: $ss = $start * $unit;
 306: list($caldb[$cnt]['dt'], $caldb[$cnt]['fmt']) = sigcalendar($ss);
 307: $cnt++;
 308: }
 309: //イベント読み込み
 310: foreach ($xml->event as $event) {
 311: $caldb[$cnt]['label'] = (string)$event->label;
 312: if (isset($event->before)) {
 313: $ss = ($start - (double)$event->before) * $unit;
 314: } else if (isset($event->after)) {
 315: $ss = (double)$event->after * $unit;
 316: } else if (isset($event->dt)) {
 317: sscanf((string)$event->dt, '%d/%d/%d %d:%d:%f', $year, $month, $day, $hour, $min, $sec);
 318: $jd = $pcl->Gregorian2JD($year, $month, $day, $hour, $min, $sec);
 319: $ss = ($start - ($jd_end - $jd) / $dd) * $unit;
 320: } else {
 321: $ss = 0;
 322: }
 323: //カレンダー計算
 324: list($caldb[$cnt]['dt'], $caldb[$cnt]['fmt']) = sigcalendar($ss);
 325: $cnt++;
 326: }
 327: $res = TRUE;
 328: }
 329: $pcl = NULL;
 330:
 331: //ソート
 332: usort($caldb, function ($a, $b) {
 333: if (! isset($a['dt'])) return NULL;
 334: if (! isset($b['dt'])) return NULL;
 335: return $a['dt'] > $b['dt'] ? (+1) : (-1);
 336: });
 337:
 338: return $res;
 339: }

個々のイベント要素を読み込む際は、年数の表記が before, after, dt によって場合分けを行い、宇宙誕生(startに記述)を基準点として、そこから何秒の距離にあるかを変数 $ss に代入する。
イベントを、基準点と、その距離に置換することで、宇宙史だけでなく、日本史や家族史のような、スケールの異なるカレンダーも作ることができる。
解説:カレンダー計算
 223: /**
 224: * カレンダー計算
 225: * @param double $ss 秒数
 226: * @return array(カレンダー,フォーマット)
 227: */
 228: function sigcalendar($ss) {
 229: $yyyy = date('Y'); //今年の西暦年
 230: $tt = (int)strtotime($yyyy .'/1/1 0:0:0') + $ss;
 231: $dt = date('m/d H:i:s', $tt);
 232:
 233: //01/01 00:00:01以前→ミリ秒まで
 234: if (preg_match('/^01\/01 01:01:01/', $dt) > 0) {
 235: $fmt = 'm/d H:i:s';
 236: $dt = date($fmt, $tt);
 237: $dt .= sprintf('.%03d', (($ss - floor($ss)) * 1000));
 238: //01/01 4時以前→秒まで
 239: } else if (preg_match('/^12\/31 0[0-4]./', $dt) > 0) {
 240: $fmt = 'm/d H:i:s';
 241: $dt = date($fmt, $tt);
 242: //01/01→分まで
 243: } else if (preg_match('/^01\/01/', $dt) > 0) {
 244: $fmt = 'm/d H:i';
 245: $dt = date($fmt, $tt);
 246: //1月→時まで
 247: } else if (preg_match('/^01/', $dt) > 0) {
 248: $fmt = 'm/d H:00';
 249: $dt = date($fmt, $tt);
 250: //12/31 23:59:59以降→ミリ秒まで
 251: } else if (preg_match('/^12\/31 23:59:59/', $dt) > 0) {
 252: $fmt = 'm/d H:i:s';
 253: $dt = date($fmt, $tt);
 254: $dt .= sprintf('.%03d', (($ss - floor($ss)) * 1000));
 255: //12/31 20時以降→秒まで
 256: } else if (preg_match('/^12\/31 2/', $dt) > 0) {
 257: $fmt = 'm/d H:i:s';
 258: $dt = date($fmt, $tt);
 259: //12/31→分まで
 260: } else if (preg_match('/^12\/31/', $dt) > 0) {
 261: $fmt = 'm/d H:i';
 262: $dt = date($fmt, $tt);
 263: //12月→時まで
 264: } else if (preg_match('/^12/', $dt) > 0) {
 265: $fmt = 'm/d H:00';
 266: $dt = date($fmt, $tt);
 267: //それ以前→日まで
 268: } else {
 269: $fmt = 'm/d';
 270: $dt = date($fmt, $tt);
 271: }
 272:
 273: return array($dt, $fmt);
 274: }
そこで、一定の境界条件を設け、カレンダーの時分秒を表示しなかったり、逆にミリ秒まで表示するようにした。
これを行うのがユーザー関数 sigcalendar である。
解説:データベースに表示用フラグを立てる
 341: /**
 342: * データベースに表示用フラグを立てる:全部
 343: * @param array $caldb データベース
 344: * @param int $ti マークしたい時刻(省略時は現在時刻)
 345: * @return なし
 346: */
 347: function checkDB_all(&$caldb, $ti=0) {
 348: if ($ti == 0) $ti = time(); //省略時
 349:
 350: $key = 0;
 351: $flag = (-1);
 352: foreach ($caldb as $rec) {
 353: if (isset($rec['dt'])) {
 354: $t0 = date($rec['fmt'], $ti);
 355: if ($rec['dt'] <$t0) {
 356: $caldb[$key]['flag'] = $flag;
 357: } else if ($flag == (-1)) {
 358: $flag++;
 359: $caldb[$key]['flag'] = $flag;
 360: $flag++;
 361: } else {
 362: $caldb[$key]['flag'] = $flag;;
 363: }
 364: $key++;
 365: }
 366: }
 367: }
 369: /**
 370: * データベースに表示用フラグを立てる:同じ月
 371: * @param array $caldb データベース
 372: * @param int $ti マークしたい時刻(省略時は現在時刻)
 373: * @return なし
 374: */
 375: function checkDB_month(&$caldb, $ti=0) {
 376: if ($ti == 0) $ti = time(); //省略時
 377: $t1 = date('m', $ti);
 378:
 379: $flag = (-2);
 380: $key = 0;
 381: foreach ($caldb as $rec) {
 382: if (isset($rec['dt'])) {
 383: $t0 = date($rec['fmt'], $ti);
 384: if ($rec['dt'] <$t0) {
 385: $caldb[$key]['flag'] = $flag;
 386: } else if ($flag == (-2)) {
 387: $caldb[$key]['flag'] = 0;
 388: $flag = (+2);
 389: } else {
 390: $caldb[$key]['flag'] = $flag;
 391: }
 392: if (preg_match('/(^\d{2})/iu', $rec['dt'], $arr) > 0) {
 393: if (($arr[1] == $t1) && ($caldb[$key]['flag']) != 0) {
 394: $caldb[$key]['flag'] = (-1);
 395: }
 396: }
 397: $key++;
 398: }
 399: }
 400: }
 402: /**
 403: * データベースに表示用フラグを立てる:「今ここ」と前後2件ずつ
 404: * @param array $caldb データベース
 405: * @param int $ti マークしたい時刻(省略時は現在時刻)
 406: * @return なし
 407: */
 408: function checkDB_now(&$caldb, $ti=0) {
 409: if ($ti == 0) $ti = time(); //省略時
 410:
 411: $flag = (-2);
 412: $key = 0;
 413: foreach ($caldb as $rec) {
 414: if (isset($rec['dt'])) {
 415: $t0 = date($rec['fmt'], $ti);
 416: if ($rec['dt'] <$t0) {
 417: $caldb[$key]['flag'] = $flag;
 418: $key++;
 419: } else if ($flag == (-2)) {
 420: $flag++;
 421: for ($i = $key - 2; $i <$key; $i++) {
 422: if (isset($caldb[$i]['label'])) $caldb[$i]['flag'] = $flag;
 423: }
 424: $flag++;
 425: $i = $key;
 426: if (isset($caldb[$i]['label'])) $caldb[$i]['flag'] = $flag;
 427: $flag++;
 428: $key++;
 429: $caldb[$key]['flag'] = $flag;
 430: $key++;
 431: if (isset($caldb[$key]['label'])) $caldb[$key]['flag'] = $flag;
 432: $key++;
 433: $flag++;
 434: } else {
 435: $caldb[$key]['flag'] = $flag;
 436: $key++;
 437: }
 438: }
 439: }
 440: }
こららを処理するのが、ユーザー関数 checkDB_all, checkDB_month, checkDB_now である。
解説:宇宙カレンダーを作成
 460: /**
 461: * 宇宙カレンダーを作成:テーブル形式
 462: * @param array $caldb データベース
 463: * @param array $caption キャプション
 464: * @return string 表示用コンテンツ
 465: */
 466: function makeCosmicCalendar_table($caldb, $caption) {
 467: $outstr =<<< EOT
 468: <table class="stripe">
 469: <caption>{$caption['label']}</caption>
 470: <tr><th>日時</th><th>イベント</th></tr>
 471:
 472: EOT;
 473:
 474: foreach ($caldb as $rec) {
 475: if ($rec['flag'] >= (-1) && $rec['flag'] = (<+1)) {
 476: $mark = ($rec['flag'] == 0) ? ' ⏱' : '';
 477: $outstr .=<<< EOT
 478: <tr>
 479: <td>{$rec['dt']}$mark</td>
 480: <td>{$rec['label']}</td>
 481: </tr>
 482:
 483: EOT;
 484: }
 485: }
 486: $outstr .=<<< EOT
 487: </table>
 488:
 489: EOT;
 490: return $outstr;
 491: }
 442: /**
 443: * 宇宙カレンダーを作成:テキスト形式
 444: * @param array $caldb データベース
 445: * @param array $caption キャプション
 446: * @return string 表示用コンテンツ
 447: */
 448: function makeCosmicCalendar_text($caldb, $caption) {
 449: $outstr = "<hr />■{$caption['label']}({$caption['description']})<br />\n";
 450: foreach ($caldb as $rec) {
 451: if ($rec['flag'] >= (-1) && $rec['flag'] = (<+1)) {
 452: $outstr .= $rec['dt'] . ' - ' . $rec['label'];
 453: if ($rec['flag'] == 0) $outstr .= '←いまココ';
 454: $outstr .= "<br />\n";
 455: }
 456: }
 457: return $outstr;
 458: }
参考サイト
- コズミックカレンダー:高校生のためのおもしろ歴史教室
- 地球人類学:文化人類学解放講座(2016年度ブログ版)
アメリカの天文学者で作家の故カール・セーガン氏が考案したとされ、パパぱふぅは学生時代、セーガン氏が進行担当したテレビ番組「コスモス」や、来日した際の講演などを聞き、当時は電卓で計算し、ノートに記入したものだ。
今回は、PHPを使い、あらかじめ用意した年表データを宇宙カレンダーに変換し、表示するプログラムを作る。なお、年表データを、たとえば日本史や家族史に置き換えれば、オリジナルの宇宙カレンダーを作ることができる。
(2022年12月30日)1月は時分まで表示するようにした,cosmicCalendar.xml更新
(2022年7月21日)バグ修正,cosmicCalendar.xml更新