PHPでヒジュラ暦付きカレンダーを作る

(1/1)
PHPで万年カレンダーを作る」で作った万年カレンダーを利用し、旧暦や六曜の代わりにヒジュラ暦(イスラム暦)を表示するPHPプログラムを作ってみる。

目次

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

PHPでヒジュラ暦付きカレンダーを作る

サンプル・プログラム

圧縮ファイルの内容
tripleCalendar.phpサンプル・プログラム本体。
pahooCalendar.php暦・潮位計算クラス pahooCalendar。
暦・潮位計算クラスの使い方は「PHPで二十四節気・七十二候一覧を作成」「PHPで月齢を計算」「PHPで日出没・月出没・月齢・潮を計算」「PHPで潮位を計算する」などを参照。include_path が通ったディレクトリに配置すること。
pahooInputData.phpデータ入力に関わる関数群。
使い方は「PHPでGET/POSTでフォームから値を受け取る」「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。
hijriCalendar.php 更新履歴
バージョン 更新日 内容
1.0.0 2024/03/17 初版
pahooCalendar.php 更新履歴
バージョン 更新日 内容
4.5.0 2024/03/17 ヒジュラ暦メソッドを追加
4.4.1 2024/03/17 getCabinetOfficeHolidayTable() -- bug-fix
4.4.0 2024/02/25 内閣府の祝日表を参照できるようにした
4.3.2 2023/02/11 getSolarTerm72() 表記改訂:水澤腹堅→水沢腹堅
4.3.1 2023/02/03 表記改訂:バクムーン→バックムーン,スタージャンムーン→スタージョンムーン,七十二候
pahooInputData.php 更新履歴
バージョン 更新日 内容
1.5.0 2024/01/28 exitIfExceedVersion() 追加
1.4.2 2024/01/28 exitIfLessVersion() メッセージ修正
1.4.1 2023/09/30 コメントの訂正
1.4.0 2023/09/09 $_GET, $_POST参照をfilter_input()関数に置換
1.3.0 2023/07/11 roundFloat() 追加

準備:初期値など

  48: //カレンダーの横幅(ピクセル)
  49: define('CALENDAR_WIDTH', 600);
  50: 
  51: //入力可能な西暦年
  52: define('YEAR_MIN', 2000);       //最小値【変更不可】
  53: define('YEAR_MAX', 2099);       //最大値【変更不可】
  54: 
  55: //入力可能な作成期間(ヶ月)
  56: define('PERIOD_MIN', 1);        //最小値【変更不可】
  57: define('PERIOD_MAX', 36);       //最大値【変更不可】
  58: define('PERIOD_DEF', 3);        //初期値
  59: 
  60: //require_once()で呼ぶファイルはinclude_pathが通っているフォルダに配置すること。
  61: //暦計算クラス
  62: require_once('pahooCalendar.php');
  63: 
  64: //データ入力に関わる関数群
  65: require_once('pahooInputData.php');
  66: 
  67: //キャッシュ保持時間(分) 0:キャッシュしない
  68: //内閣府へのアクセス負荷軽減のため,1440分(24時間)以上のキャッシュ保持をお勧めします.
  69: define('LIFE_CACHE', 1440);
  70: 
  71: //キャッシュ・ディレクトリ
  72: //書き込み可能で,外部からアクセスされないディレクトリを指定してください.
  73: //最大150Mバイトを消費します.
  74: define('DIR_CACHE', './pcacheholiday/');
  75: 
  76: //キャッシュ処理に関わるクラス:include_pathが通ったディレクトリに配置
  77: require_once('pahooCache.php');

各種定数は変更可能である。
データ入力に関わる関数群 "pahooInputData.php" および暦計算クラス・ファイル "pahooCalendar.php" はinclude_pathが通ったディレクトリに配置すること。

ヒジュラ暦とは

イスラム教のムハンマド(マホメット)は、西暦622年にメッカからメディナへヒジュラ(聖遷)した。ムハンマドの死後、後継者として2代目カリフに就任したウマルは、遡って622年7月16日を期限元年1月1日とするヒジュラ暦を定めた。

コーラン(クルアーン)に「本当にアッラーの御許で、(1年の)月数は、12か月である」(第9章36節)、「本当に(聖月を)延ばすことは、不信心を増長させ、それで不信者は誤って導かれている。ある年は(聖月を)普通の月とし、(他の年は)聖月とする」(同37節)と記されていることから、ヒジュラ暦は月の満ち欠けに基づく純粋な太陰暦で、しかも新月ではなく細い三日月の日を月の第1日とする。日本の旧暦のように季節の調整をする太陰太陽暦と異なる独特な暦であり、われわれが現在使っているグレゴリオ暦から単純に換算することができない。

月の満ち欠けの周期は約29.5日(朔望月 (さくぼうげつ) )で、ヒジュラ暦では29日の月と30日の月が互い違いに並ぶ。1年は354日であり、グレゴリオ暦よりも11日も短い。
そして、朔望月の小数点以下は0.5日=12時間ではなく、12時間44分である。このため、1年間で44分×12=8時間48分のズレが生じる。これを補正するため、年によっては最後の月を29日ではなく30日とする追加日を設ける(うるう日とは呼ばない)。
標準的なヒジュラ暦では、30年周期で11回――2, 5, 7, 10, 13, 16, 18, 21, 24, 26, 29年目――の追加日を置くが、この置き方は国や地域によって異なる。

季節と無関係に刻まれるカレンダーというのは、われわれにとっては奇異なものに見える。
これは想像だが、原初のイスラム宗教圏には農耕民族が少なく、季節に合わせたカレンダーを作ることより、戒律を優先したのではないだろうが。

解説:ユリウス日からヒジュラ暦を求める

 555: /**
 556:  * ユリウス日からヒジュラ暦を求める.
 557:  * 参考サイト https://www.pahoo.org/e-soul/webtech/php02/php02-61-01.shtm
 558:  * @param   float $jd ユリウス日
 559:  * @return  array(ヒジュラ暦の年,月,日)
 560: */
 561: function JD2Hijri($jd) {
 562:     //ヒジュラ暦の基準日(グレゴリオ暦622年7月16日)をユリウス日で表した値
 563:     $baseJD = 1948440;
 564: 
 565:     $l = $this->interHijri($jd - $baseJD + 10633);
 566:     $n = $this->interHijri(($l - 1) / 10631);
 567:     $l = $l - 10631 * $n + 354;
 568:     $j = $this->interHijri((10985 - $l) / 5316* $this->interHijri((50 * $l) / 17719+ $this->interHijri($l / 5670* $this->interHijri((43 * $l) / 15238);
 569:     $l = $l - $this->interHijri((30 - $j) / 15* $this->interHijri((17719 * $j) / 50- $this->interHijri($j / 16* $this->interHijri((15238 * $j) / 43+ 29;
 570: 
 571:     //ヒジュラ暦の月
 572:     $hijriMonth = (int)($this->interHijri((24 * $l) / 709));
 573:     //ヒジュラ暦の日
 574:     $hijriDay = (int)($l - $this->interHijri((709 * $hijriMonth) / 24));
 575:     //ヒジュラ暦の年
 576:     $hijriYear = (int)(30 * $n + $j - 30);
 577: 
 578:     return array($hijriYear, $hijriMonth, $hijriDay);
 579: }

 546: /**
 547:  * 演算誤差修正用
 548:  * @param   float $x
 549:  * @return  int 結果
 550: */
 551: function interHijri($x) {
 552:     return ($x < -0.0000001? ceil($x - 0.0000001: floor($x + 0.0000001);
 553: }

西暦をヒジュラ暦に変換する方法を考える。
622年7月16日から月の満ち欠けを計算するのは、計算量が膨大になる。そこで、ユリウス日を使って、622年7月16日からの通日を求めるところから始めることにした。
JD2Hijri はユリウス日から、ヒジュラ暦の年、月、日を求めるメソッドである。

解説:西暦からヒジュラ暦を求める

 581: /**
 582:  * 西暦からヒジュラ暦を求める.
 583:  * 1581年以前はユリウス暦,1582年以降はグレゴリオ暦として計算する.
 584:  * 参考サイト https://www.pahoo.org/e-soul/webtech/php02/php02-61-01.shtm
 585:  * @param   int $year, $month, $day  西暦による年月日
 586:  * @return  array(ヒジュラ暦の年,月,日)
 587: */
 588: function AD2Hijri($year, $month, $day) {
 589:     $jd = $this->AD2JD($year, $month, $day);
 590: 
 591:     return $this->JD2Hijri($jd);
 592: }

西暦をヒジュラ暦を求めるには、AD2JD メソッドを使って西暦をユリウス日に変換し、上述のメソッド JD2Hijri に通す。

解説:ラマダン月を求める

 594: /**
 595:  * ラマダン月かどうかを判定する.
 596:  * 参考サイト https://www.pahoo.org/e-soul/webtech/php02/php02-61-01.shtm
 597:  * @param   int $year, $month, $day  ヒジュラ暦による年月日
 598:  * @return  bool TRUE:ラマダン月である/FALSE:ではない
 599: */
 600: function isRamadan($year, $month, $day) {
 601:     return ($month == 9);
 602: }

ヒジュラ暦では各月に名前が付いており、第9月がラマダン月である。この月の日の出から日没までの間、イスラム教徒は義務の一つとして「断食(サウム)」を行う。

解説:カレンダー作成

 185: /**
 186:  * 1ヶ月分のカレンダーを作成
 187:  * @param   object $pcl  pahooCalendarオブジェクト
 188:  * @param   int $start 週の開始曜日(0:日曜日, 1:月曜日...6:土曜日)
 189:  * @param   int $year  西暦年
 190:  * @param   int $month 月
 191:  * @return  string HTMLコンテンツ/FALSE:エラー
 192: */
 193: function makeCalendar($pcl, $start, $year, $month) {
 194:     //月のエラーチェック
 195:     if ($month < 1 && $month > 12)       return FALSE;
 196: 
 197:     $html =<<< EOT
 198: <table class="calendar">
 199: <tr>
 200: <th colspan="7"><span class="large">{$year}年 {$month}月</span></th>
 201: </tr>
 202: <tr>
 203: 
 204: EOT;
 205: 
 206:     //曜日の行
 207:     for ($i = 0$i < 7$i++) {
 208:         $n  = ($start + $i% 7;
 209:         if ($n == 6)        $class = 'blue';
 210:         else if ($n == 0)   $class = 'red';
 211:         else                $class = 'black';
 212:         $str = $pcl->__getWeekString($n);
 213:         $html ."<th><span class=\"{$class}\">{$str}</span></th>";
 214:     }
 215:     $html ."</tr>\n";
 216: 
 217:     //カレンダー本体
 218:     $wn1 = $pcl->getWeekNumber($year, $month, 1);   //月の最初の曜日
 219:     $dim = $pcl->getDaysInMonth($year, $month);     //月の最後の日
 220:     $cnt = 0;
 221:     $flag = FALSE;
 222:     $n = $start;
 223:     $day = 1;
 224:     while (1) {
 225:         if ($cnt % 7 == 0)      $html ."<tr>\n";
 226:         //曜日の色
 227:         if ($n % 7 == 6)        $class = 'blue';
 228:         else if ($n % 7 == 0)   $class = 'red';
 229:         else                    $class = 'black';
 230:         if ($n % 7 == $wn1)     $flag = TRUE;
 231:         //表示開始
 232:         if ($flag) {
 233:             //祝日
 234:             $holiday = $pcl->getHoliday($year, $month, $day);
 235:             //ヒジュラ暦
 236:             list($hy, $hm, $hd) = $pcl->AD2Hijri($year, $month, $day);
 237:             $hijri = sprintf('%d年<br>%d月%d日', $hy, $hm, $hd);
 238:             $color = $pcl->isRamadan($hy, $hm, $hd? 'orange' : 'green';
 239: 
 240:             //表示
 241:             $html .=<<< EOT
 242: <td>
 243: <span class="large {$class}">{$day}</span>
 244: <div class="narrow">
 245: <span class="small red">{$holiday}</span><br><br>
 246: <span class="small {$color}">{$hijri}</span>
 247: </div>
 248: </td>
 249: 
 250: EOT;
 251:             $day++;
 252:             if ($day > $dim)    break;
 253:         } else {
 254:             $html ."<td>&nbsp;</td>";
 255:         }
 256:         $cnt++;
 257:         $n++;
 258:         if ($cnt % 7 == 0)  $html ."</tr>\n";
 259:     }
 260:     //最後の日以降の処理
 261:     $cnt++;
 262:     while ($cnt % 7 !0) {
 263:         $html ."<td>&nbsp;</td>";
 264:         $cnt++;
 265:         if ($cnt % 7 == 0)  $html ."</tr>\n";
 266:     }
 267: 
 268:     $html ."</table>\n";
 269: 
 270:     return $html;
 271: }

ユーザー関数 makeCalendar は1ヶ月分のカレンダーを作成する。
週の開始曜日を自由に設定できるようにするための工夫をしている。

カレンダー本体を作成する処理では、まず、月の最初の曜日 $wn1 と、月の最後の日 $dim を計算しておく。
$wn1 と週の開始曜日 $start が一致するまでは変数 $flag がFALSEのままで、この時は日付を入れずに空のままにしておく。
$flag がTRUEになったら日付を入れてゆく。あわせて、祝日とヒジュラ暦の計算を行う。
$dim に達したらループを抜け出し、行末まで空白のセルを追加してゆく。

参考サイト

(この項おわり)
header