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

サンプル・プログラム
cosmicCalendar.php | サンプル・プログラム本体。 |
cosmicCalendar.xml | イベント・ファイル。 |
pahooCalendar.php | 暦計算クラス pahooCalendar。 暦計算クラスの使い方は「PHPで日出没・月出没・月齢・潮を計算」を参照。include_path が通ったディレクトリに配置すること。 |
イベント・ファイル
同梱したファイルには、宇宙の誕生から現在までの主要イベントを記入済みである。
このイベント・ファイルを編集すれば、日本史や家族史を使った宇宙カレンダー(365日カレンダーと呼んだ方がいいかもしれない)を表示することができる。
0001: <?xml version="1.0" encoding="utf-8" ?>
0002: <!-- 年代記 -->
0003: <cosmicCalendar>
0004: <!-- キャプション -->
0005: <caption>
0006: <label>宇宙カレンダー</label>
0007: <description>宇宙誕生から現在までを1年に圧縮</description>
0008: </caption>
0009: <!-- 開始 -->
0010: <start>
0011: <label>宇宙誕生</label>
0012: <before unit="year">138E+8</before>
0013: <description></description>
0014: </start>
0015: <finish>
0016: <label>現在</label>
0017: <before unit="year">0</before>
0018: <description></description>
0019: </finish>
0020:
0021: <!-- 以下、イベント・データベース -->
0022: <event>
0023: <label>宇宙の晴れ上がり</label>
0024: <!-- 開始イベントからの年数を記載するときはafter -->
0025: <after unit="year">30E+4</after>
0026: <description></description>
0027: </event>
0028: <event>
0029: <label>最初の恒星</label>
0030: <!-- 現在から××年前を記載するときはbefore -->
0031: <before unit="year">135E+8</before>
0032: <description></description>
0033: </event>
0034: <event>
0035: <label>太平洋戦争終結</label>
0036: <!-- 西暦記載するときはdtでyyyy/mm/dd hh:mm:ss形式 -->
0037: <dt>1945/8/15 00:00:00</dt>
0038: <description></description>
0039: </event>
0040: <event>
0041: <label>ハンムラビ王の即位</label>
0042: <!-- 紀元前はマイナス表記 -->
0043: <dt>-1792/1/1 00:00:00</dt>
0044: <description></description>
0045: </event>
0046: <event>
0047: <label>最初の銀河の形成</label>
0048: <before unit="year">132E+8</before>
0049: <description></description>
0050: </event>
準備:外部クラス
0044: //暦計算クラス
0045: require_once('pahooCalendar.php');
そこで、クラスファイル "pahooCalendar.php" を require_once し、オブジェクトを生成する。
解説:イベント・ファイルを読み込み、ソートする
0259: /**
0260: * イベント・ファイルを読み込み、ソートする
0261: * @param string $fnameイベント・ファイル名
0262: * @param array $caldbイベントを格納する配列
0263: * @param array $captionキャプションを格納する配列
0264: * @return bool TRUE/FALSE
0265: */
0266: function readDB($fname, &$caldb, &$caption) {
0267: $pcl = new pahooCalendar(); //pahooCalendarクラス
0268: $pcl->setLanguage('jp');
0269:
0270: $jd_end = $pcl->Gregorian2JD(date('Y') + 1, 1, 1, 0, 0, 0); //今年の最後
0271: $dd = 365 + date('L'); //1年の日数
0272: $res = FALSE;
0273:
0274: //イベント・ファイル読み込み
0275: if (file_exists($fname)) {
0276: $xml = simplexml_load_file($fname);
0277: //キャプション
0278: if (isset($xml->caption->label)) {
0279: $caption['label'] = isset($xml->caption->label) ? (string)$xml->caption->label : '宇宙カレンダー';
0280: $caption['description'] = isset($xml->caption->description) ? (string)$xml->caption->description : '宇宙誕生から現在までを1年に圧縮';
0281: }
0282: //開始
0283: if (isset($xml->start->before)) {
0284: $start = (double)$xml->start->before;
0285: $unit = ($dd * 24 * 60 * 60) / $start; //1秒当たり年数
0286: $cnt = 0;
0287: $caldb[$cnt]['label'] = (string)$xml->start->label;
0288: $ss = $start * $unit;
0289: list($caldb[$cnt]['dt'], $caldb[$cnt]['fmt']) = sigcalendar($ss);
0290: $cnt++;
0291: }
0292: //イベント読み込み
0293: foreach ($xml->event as $event) {
0294: $caldb[$cnt]['label'] = (string)$event->label;
0295: if (isset($event->before)) {
0296: $ss = ($start - (double)$event->before) * $unit;
0297: } else if (isset($event->after)) {
0298: $ss = (double)$event->after * $unit;
0299: } else if (isset($event->dt)) {
0300: sscanf((string)$event->dt, '%d/%d/%d %d:%d:%f', $year, $month, $day, $hour, $min, $sec);
0301: $jd = $pcl->Gregorian2JD($year, $month, $day, $hour, $min, $sec);
0302: $ss = ($start - ($jd_end - $jd) / $dd) * $unit;
0303: } else {
0304: $ss = 0;
0305: }
0306: //カレンダー計算
0307: list($caldb[$cnt]['dt'], $caldb[$cnt]['fmt']) = sigcalendar($ss);
0308: $cnt++;
0309: }
0310: $res = TRUE;
0311: }
0312: $pcl = NULL;
0313:
0314: //ソート
0315: usort($caldb, function ($a, $b) {
0316: if (! isset($a['dt'])) return NULL;
0317: if (! isset($b['dt'])) return NULL;
0318: return $a['dt'] > $b['dt'] ? (+1) : (-1);
0319: });
0320:
0321: return $res;
0322: }

個々のイベント要素を読み込む際は、年数の表記が before, after, dt によって場合分けを行い、宇宙誕生(startに記述)を基準点として、そこから何秒の距離にあるかを変数 $ss に代入する。
イベントを、基準点と、その距離に置換することで、宇宙史だけでなく、日本史や家族史のような、スケールの異なるカレンダーも作ることができる。
解説:カレンダー計算
0223: /**
0224: * カレンダー計算
0225: * @param double $ss 秒数
0226: * @return array(カレンダー,フォーマット)
0227: */
0228: function sigcalendar($ss) {
0229: $yyyy = date('Y'); //今年の西暦年
0230: $tt = strtotime($yyyy .'/1/1 0:0:0') + $ss;
0231: $dt = date('m/d H:i:s', $tt);
0232:
0233: //12/31 23:59:59以降→ミリ秒まで
0234: if (preg_match('/^12\/31 23:59:59/', $dt) > 0) {
0235: $fmt = 'm/d H:i:s';
0236: $dt = date($fmt, $tt);
0237: $dt .= sprintf('.%03d', (($ss - floor($ss)) * 1000));
0238: //12/31 20時以降→秒まで
0239: } else if (preg_match('/^12\/31 2/', $dt) > 0) {
0240: $fmt = 'm/d H:i:s';
0241: $dt = date($fmt, $tt);
0242: //12/31→分まで
0243: } else if (preg_match('/^12\/31/', $dt) > 0) {
0244: $fmt = 'm/d H:i';
0245: $dt = date($fmt, $tt);
0246: //12月→時まで
0247: } else if (preg_match('/^12/', $dt) > 0) {
0248: $fmt = 'm/d H:00';
0249: $dt = date($fmt, $tt);
0250: //それ以前→日まで
0251: } else {
0252: $fmt = 'm/d';
0253: $dt = date($fmt, $tt);
0254: }
0255:
0256: return array($dt, $fmt);
0257: }
そこで、一定の境界条件を設け、カレンダーの時分秒を表示しなかったり、逆にミリ秒まで表示するようにした。
これを行うのがユーザー関数 sigcalendar である。
解説:データベースに表示用フラグを立てる
0324: /**
0325: * データベースに表示用フラグを立てる:全部
0326: * @param array $caldbデータベース
0327: * @param int $ti マークしたい時刻(省略時は現在時刻)
0328: * @return なし
0329: */
0330: function checkDB_all(&$caldb, $ti=0) {
0331: if ($ti == 0) $ti = time(); //省略時
0332:
0333: $key = 0;
0334: $flag = (-1);
0335: foreach ($caldb as $rec) {
0336: if (isset($rec['dt'])) {
0337: $t0 = date($rec['fmt'], $ti);
0338: if ($rec['dt'] < $t0) {
0339: $caldb[$key]['flag'] = $flag;
0340: } else if ($flag == (-1)) {
0341: $flag++;
0342: $caldb[$key]['flag'] = $flag;
0343: $flag++;
0344: } else {
0345: $caldb[$key]['flag'] = $flag;;
0346: }
0347: $key++;
0348: }
0349: }
0350: }
0352: /**
0353: * データベースに表示用フラグを立てる:同じ月
0354: * @param array $caldbデータベース
0355: * @param int $ti マークしたい時刻(省略時は現在時刻)
0356: * @return なし
0357: */
0358: function checkDB_month(&$caldb, $ti=0) {
0359: if ($ti == 0) $ti = time(); //省略時
0360: $t1 = date('m', $ti);
0361:
0362: $flag = (-2);
0363: $key = 0;
0364: foreach ($caldb as $rec) {
0365: if (isset($rec['dt'])) {
0366: $t0 = date($rec['fmt'], $ti);
0367: if ($rec['dt'] < $t0) {
0368: $caldb[$key]['flag'] = $flag;
0369: } else if ($flag == (-2)) {
0370: $caldb[$key]['flag'] = 0;
0371: $flag = (+2);
0372: } else {
0373: $caldb[$key]['flag'] = $flag;
0374: }
0375: if (preg_match('/(^\d{2})/iu', $rec['dt'], $arr) > 0) {
0376: if (($arr[1] == $t1) && ($caldb[$key]['flag']) != 0) {
0377: $caldb[$key]['flag'] = (-1);
0378: }
0379: }
0380: $key++;
0381: }
0382: }
0383: }
0385: /**
0386: * データベースに表示用フラグを立てる:「今ここ」と前後2件ずつ
0387: * @param array $caldbデータベース
0388: * @param int $ti マークしたい時刻(省略時は現在時刻)
0389: * @return なし
0390: */
0391: function checkDB_now(&$caldb, $ti=0) {
0392: if ($ti == 0) $ti = time(); //省略時
0393:
0394: $flag = (-2);
0395: $key = 0;
0396: foreach ($caldb as $rec) {
0397: if (isset($rec['dt'])) {
0398: $t0 = date($rec['fmt'], $ti);
0399: if ($rec['dt'] < $t0) {
0400: $caldb[$key]['flag'] = $flag;
0401: $key++;
0402: } else if ($flag == (-2)) {
0403: $flag++;
0404: for ($i = $key - 2; $i < $key; $i++) {
0405: if (isset($caldb[$i]['label'])) $caldb[$i]['flag'] = $flag;
0406: }
0407: $flag++;
0408: $i = $key;
0409: if (isset($caldb[$i]['label'])) $caldb[$i]['flag'] = $flag;
0410: $flag++;
0411: $key++;
0412: $caldb[$key]['flag'] = $flag;
0413: $key++;
0414: if (isset($caldb[$key]['label'])) $caldb[$key]['flag'] = $flag;
0415: $key++;
0416: $flag++;
0417: } else {
0418: $caldb[$key]['flag'] = $flag;
0419: $key++;
0420: }
0421: }
0422: }
0423: }
こららを処理するのが、ユーザー関数 checkDB_all, checkDB_month, checkDB_now である。
解説:宇宙カレンダーを作成
0443: /**
0444: * 宇宙カレンダーを作成:テーブル形式
0445: * @param array $caldbデータベース
0446: * @param array $captionキャプション
0447: * @return string 表示用コンテンツ
0448: */
0449: function makeCosmicCalendar_table($caldb, $caption) {
0450: $outstr =<<< EOT
0451: <table class="stripe">
0452: <caption>{$caption['label']}</caption>
0453: <tr><th>日時</th><th>イベント</th></tr>
0454:
0455: EOT;
0456:
0457: foreach ($caldb as $rec) {
0458: if ($rec['flag'] >= (-1) && $rec['flag'] <= (+1)) {
0459: $mark = ($rec['flag'] == 0) ? ' ⏱' : '';
0460: $outstr .=<<< EOT
0461: <tr>
0462: <td>{$rec['dt']}$mark</td>
0463: <td>{$rec['label']}</td>
0464: </tr>
0465:
0466: EOT;
0467: }
0468: }
0469: $outstr .=<<< EOT
0470: </table>
0471:
0472: EOT;
0473: return $outstr;
0474: }
0425: /**
0426: * 宇宙カレンダーを作成:テキスト形式
0427: * @param array $caldb データベース
0428: * @param array $captionキャプション
0429: * @return string 表示用コンテンツ
0430: */
0431: function makeCosmicCalendar_text($caldb, $caption) {
0432: $outstr = "<hr />■{$caption['label']}({$caption['description']})<br />\n";
0433: foreach ($caldb as $rec) {
0434: if ($rec['flag'] >= (-1) && $rec['flag'] <= (+1)) {
0435: $outstr .= $rec['dt'] . ' - ' . $rec['label'];
0436: if ($rec['flag'] == 0) $outstr .= '←いまココ';
0437: $outstr .= "<br />\n";
0438: }
0439: }
0440: return $outstr;
0441: }
参考サイト
- コズミックカレンダー:高校生のためのおもしろ歴史教室
- 地球人類学:文化人類学解放講座(2016年度ブログ版)
アメリカの天文学者で作家の故カール・セーガン氏が考案したとされ、パパぱふぅは学生時代、セーガン氏が進行担当したテレビ番組「コスモス」や、来日した際の講演などを聞き、当時は電卓で計算し、ノートに記入したものだ。
今回は、PHPを使い、あらかじめ用意した年表データを宇宙カレンダーに変換し、表示するプログラムを作る。なお、年表データを、たとえば日本史や家族史に置き換えれば、オリジナルの宇宙カレンダーを作ることができる。
(2021年5月12日)バグ修正
(2021年1月16日)PHP8対応