サンプル・プログラムの実行例
サンプル・プログラム
getMoonAge.php | サンプル・プログラム本体。 |
pahooCalendar.php | 暦・潮位計算クラス pahooCalendar。 暦・潮位計算クラスの使い方は「PHPで二十四節気・七十二候一覧を作成」「PHPで月齢を計算」「PHPで日出没・月出没・月齢・潮を計算」「PHPで潮位を計算する」などを参照。include_path が通ったディレクトリに配置すること。 |
pahooInputData.php | データ入力に関わる関数群。 使い方は「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。 |
バージョン | 更新日 | 内容 |
---|---|---|
1.6.0 | 2023/01/14 | 数値入力にSpinner、pahooInputData導入 |
1.5.0 | 2023/01/11 | 計算精度向上,calcMoonAge()で時刻指定可能に |
1.4 | 2021/05/08 | PHP8対応,リファラ・チェック改良 |
1.3 | 2020/01/02 | リファラチェック追加 |
1.2 | 2018/01/03 | pahooCalendarクラス利用 |
バージョン | 更新日 | 内容 |
---|---|---|
4.3.2 | 2023/02/11 | getSolarTerm72() 表記改訂:水澤腹堅→水沢腹堅 |
4.3.1 | 2023/02/03 | 表記改訂:バクムーン→バックムーン,スタージャンムーン→スタージョンムーン,七十二候 |
4.3.0 | 2023/01/14 | コメント表記などを見直した,tenshanichi()追加 |
4.2.0 | 2023/01/11 | getTimeDifference(),setTimeDifference()追加 |
4.1.0 | 2023/01/09 | 太陽,月の位置計算の基準をUTCに変更した |
バージョン | 更新日 | 内容 |
---|---|---|
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() 追加 |
準備:初期値など
36: //指定できる西暦年の範囲
37: define('MIN_YEAR', 1901);
38: define('MAX_YEAR', 2099);
39:
40: //世界時からの時差(日本標準時)
41: define('UTCDIFF', +9.0);
42:
43: //計算期間(月)
44: define('CALC_TERM', 3);
45:
46: //月齢を計算する時刻
47: define('CALC_HOUR', 21);
48:
49: //Spinner - jQuery UI を使用するかどうか
50: define('USESPINNER', TRUE);
51:
52: //数値増減クリックで即判定するかどうか(TRUE:即判定,FALE:判定ボタンを用意)
53: define('ONCHANGE', FALSE);
54:
55: //表示幅(単位:ピクセル)
56: define('WIDTH', 550);
57:
58: //require_once()で呼ぶファイルはinclude_pathが通っているフォルダに配置すること.
59: //暦計算クラス
60: require_once('pahooCalendar.php');
61:
62: //データ入力に関わる関数群
63: require_once('pahooInputData.php');
月齢、月の視半径、各種カレンダー計算は、ユーザークラス "pahooCalendar" に用意したメソッド群を利用する。
オブジェクト生成時に、表示言語として "ja"(日本語)を、世界時との時差としてユーザー定義の定数 UTCDIFF を渡しておく。こうすることで、pahooCalendar クラスが出力する文字列は日本語に、日時計算は時差 UTCDIFF がある前提で計算を行う。
数値入力に jQuery UI の Spinner を使用している。設定でOFFにすることもできる。Spinner の使い方については、「Spinner - jQuery UI - PHPで素数かどうか判定」をご覧いただきたい。
入力した数値の取得とバリデーションチェックに、ユーザー定義クラス群 "pahooInputData.php" を利用している。
月齢とは
月の位置を黄経・黄緯で示すとき、太陽の黄経が0度であれば朔(新月)となる。朔の瞬間を月齢0.0とし、そこから1日後(24時間後)は1.0、2日後は2.0‥‥とカウントしていく。
そこで、今回は月の視黄経を求める必要がある。
解説:月の視黄経の算出
1391: /**
1392: * J2000.0からの経過年数における月の視黄経(度)を求める。
1393: * 参考サイト https://www.pahoo.org/e-soul/webtech/php02/php02-44-01.shtm
1394: * @param float $jy 2000.0からの経過年数
1395: * @return float 月の視黄経(度)
1396: */
1397: function __longitude_moon($jy) {
1398: $am = 0.0006 * sin(deg2rad($this->__angle( 54.0 + 19.3 * (float)$jy)));
1399: $am += 0.0006 * sin(deg2rad($this->__angle( 71.0 + 0.2 * (float)$jy)));
1400: $am += 0.0020 * sin(deg2rad($this->__angle( 55.0 + 19.34 * (float)$jy)));
1401: $am += 0.0040 * sin(deg2rad($this->__angle(119.5 + 1.33 * (float)$jy)));
1402: $rm_moon = 0.0003 * sin(deg2rad($this->__angle(280.0 + 23221.3 * $jy)));
1403: $rm_moon += 0.0003 * sin(deg2rad($this->__angle(161.0 + 40.7 * $jy)));
1404: $rm_moon += 0.0003 * sin(deg2rad($this->__angle(311.0 + 5492.0 * $jy)));
1405: $rm_moon += 0.0003 * sin(deg2rad($this->__angle(147.0 + 18089.3 * $jy)));
1406: $rm_moon += 0.0003 * sin(deg2rad($this->__angle( 66.0 + 3494.7 * $jy)));
1407: $rm_moon += 0.0003 * sin(deg2rad($this->__angle( 83.0 + 3814.0 * $jy)));
1408: $rm_moon += 0.0004 * sin(deg2rad($this->__angle( 20.0 + 720.0 * $jy)));
1409: $rm_moon += 0.0004 * sin(deg2rad($this->__angle( 71.0 + 9584.7 * $jy)));
1410: $rm_moon += 0.0004 * sin(deg2rad($this->__angle(278.0 + 120.1 * $jy)));
1411: $rm_moon += 0.0004 * sin(deg2rad($this->__angle(313.0 + 398.7 * $jy)));
1412: $rm_moon += 0.0005 * sin(deg2rad($this->__angle(332.0 + 5091.3 * $jy)));
1413: $rm_moon += 0.0005 * sin(deg2rad($this->__angle(114.0 + 17450.7 * $jy)));
1414: $rm_moon += 0.0005 * sin(deg2rad($this->__angle(181.0 + 19088.0 * $jy)));
1415: $rm_moon += 0.0005 * sin(deg2rad($this->__angle(247.0 + 22582.7 * $jy)));
1416: $rm_moon += 0.0006 * sin(deg2rad($this->__angle(128.0 + 1118.7 * $jy)));
1417: $rm_moon += 0.0007 * sin(deg2rad($this->__angle(216.0 + 278.6 * $jy)));
1418: $rm_moon += 0.0007 * sin(deg2rad($this->__angle(275.0 + 4853.3 * $jy)));
1419: $rm_moon += 0.0007 * sin(deg2rad($this->__angle(140.0 + 4052.0 * $jy)));
1420: $rm_moon += 0.0008 * sin(deg2rad($this->__angle(204.0 + 7906.7 * $jy)));
1421: $rm_moon += 0.0008 * sin(deg2rad($this->__angle(188.0 + 14037.3 * $jy)));
1422: $rm_moon += 0.0009 * sin(deg2rad($this->__angle(218.0 + 8586.0 * $jy)));
1423: $rm_moon += 0.0011 * sin(deg2rad($this->__angle(276.5 + 19208.02 * $jy)));
1424: $rm_moon += 0.0012 * sin(deg2rad($this->__angle(339.0 + 12678.71 * $jy)));
1425: $rm_moon += 0.0016 * sin(deg2rad($this->__angle(242.2 + 18569.38 * $jy)));
1426: $rm_moon += 0.0018 * sin(deg2rad($this->__angle( 4.1 + 4013.29 * $jy)));
1427: $rm_moon += 0.0020 * sin(deg2rad($this->__angle( 55.0 + 19.34 * $jy)));
1428: $rm_moon += 0.0021 * sin(deg2rad($this->__angle(105.6 + 3413.37 * $jy)));
1429: $rm_moon += 0.0021 * sin(deg2rad($this->__angle(175.1 + 719.98 * $jy)));
1430: $rm_moon += 0.0021 * sin(deg2rad($this->__angle( 87.5 + 9903.97 * $jy)));
1431: $rm_moon += 0.0022 * sin(deg2rad($this->__angle(240.6 + 8185.36 * $jy)));
1432: $rm_moon += 0.0024 * sin(deg2rad($this->__angle(252.8 + 9224.66 * $jy)));
1433: $rm_moon += 0.0024 * sin(deg2rad($this->__angle(211.9 + 988.63 * $jy)));
1434: $rm_moon += 0.0026 * sin(deg2rad($this->__angle(107.2 + 13797.39 * $jy)));
1435: $rm_moon += 0.0027 * sin(deg2rad($this->__angle(272.5 + 9183.99 * $jy)));
1436: $rm_moon += 0.0037 * sin(deg2rad($this->__angle(349.1 + 5410.62 * $jy)));
1437: $rm_moon += 0.0039 * sin(deg2rad($this->__angle(111.3 + 17810.68 * $jy)));
1438: $rm_moon += 0.0040 * sin(deg2rad($this->__angle(119.5 + 1.33 * $jy)));
1439: $rm_moon += 0.0040 * sin(deg2rad($this->__angle(145.6 + 18449.32 * $jy)));
1440: $rm_moon += 0.0040 * sin(deg2rad($this->__angle( 13.2 + 13317.34 * $jy)));
1441: $rm_moon += 0.0048 * sin(deg2rad($this->__angle(235.0 + 19.34 * $jy)));
1442: $rm_moon += 0.0050 * sin(deg2rad($this->__angle(295.4 + 4812.66 * $jy)));
1443: $rm_moon += 0.0052 * sin(deg2rad($this->__angle(197.2 + 319.32 * $jy)));
1444: $rm_moon += 0.0068 * sin(deg2rad($this->__angle( 53.2 + 9265.33 * $jy)));
1445: $rm_moon += 0.0079 * sin(deg2rad($this->__angle(278.2 + 4493.34 * $jy)));
1446: $rm_moon += 0.0085 * sin(deg2rad($this->__angle(201.5 + 8266.71 * $jy)));
1447: $rm_moon += 0.0100 * sin(deg2rad($this->__angle( 44.89 + 14315.966 * $jy)));
1448: $rm_moon += 0.0107 * sin(deg2rad($this->__angle(336.44 + 13038.696 * $jy)));
1449: $rm_moon += 0.0110 * sin(deg2rad($this->__angle(231.59 + 4892.052 * $jy)));
1450: $rm_moon += 0.0125 * sin(deg2rad($this->__angle(141.51 + 14436.029 * $jy)));
1451: $rm_moon += 0.0153 * sin(deg2rad($this->__angle(130.84 + 758.698 * $jy)));
1452: $rm_moon += 0.0305 * sin(deg2rad($this->__angle(312.49 + 5131.979 * $jy)));
1453: $rm_moon += 0.0348 * sin(deg2rad($this->__angle(117.84 + 4452.671 * $jy)));
1454: $rm_moon += 0.0410 * sin(deg2rad($this->__angle(137.43 + 4411.998 * $jy)));
1455: $rm_moon += 0.0459 * sin(deg2rad($this->__angle(238.18 + 8545.352 * $jy)));
1456: $rm_moon += 0.0533 * sin(deg2rad($this->__angle( 10.66 + 13677.331 * $jy)));
1457: $rm_moon += 0.0572 * sin(deg2rad($this->__angle(103.21 + 3773.363 * $jy)));
1458: $rm_moon += 0.0588 * sin(deg2rad($this->__angle(214.22 + 638.635 * $jy)));
1459: $rm_moon += 0.1143 * sin(deg2rad($this->__angle( 6.546 + 9664.0404 * $jy)));
1460: $rm_moon += 0.1856 * sin(deg2rad($this->__angle(177.525 + 359.9905 * $jy)));
1461: $rm_moon += 0.2136 * sin(deg2rad($this->__angle(269.926 + 9543.9773 * $jy)));
1462: $rm_moon += 0.6583 * sin(deg2rad($this->__angle(235.700 + 8905.3422 * $jy)));
1463: $rm_moon += 1.2740 * sin(deg2rad($this->__angle(100.738 + 4133.3536 * $jy)));
1464: $rm_moon += 6.2887 * sin(deg2rad($this->__angle(134.961 + 4771.9886 * $jy + $am)));
1465:
1466: return (float)$this->__angle($rm_moon + 218.3161 + 4812.67881 * $jy);
1467: }
1469: /**
1470: * 指定した日時における月の視黄経(度)を求める(日時はローカル時間).
1471: * 参考サイト https://www.pahoo.org/e-soul/webtech/php02/php02-44-01.shtm
1472: * @param int $year, $month, $day 年月日
1473: * @param float $hour, $min, $sec 時分秒(日本時)
1474: * @param float $tdiff UTCとの時差;NULLの時はTDIFF
1475: * @return float 月の視黄経(度)
1476: */
1477: function longitude_moon($year, $month, $day, $hour, $min, $sec, $tdiff=NULL) {
1478: $jy = (float)$this->Gregorian2JY($year, $month, $day, $hour, $min, $sec, $tdiff);
1479:
1480: return $this->__longitude_moon($jy);
1481: }
解説:次の朔の日時を求める
1728: /**
1729: * 指定した日時の次の新月の日時を求める(日時はローカル時間).
1730: * 太陽と月の黄緯差0度になる日時を近似計算で求める.
1731: * 参考サイト https://www.pahoo.org/e-soul/webtech/php02/php02-43-01.shtm
1732: * @param int $year, $month, $day 年月日(グレゴリオ暦)
1733: * @param float $hour, $min, $sec 時分秒(ローカル時間)
1734: * @param float $tdiff UTCとの時差;NULLの時はTDIFF
1735: * @param int $method 引き込み方式(1または2)
1736: * @return array($year, $month, $day, $hour, $min, $sec) 日時(ローカル時間)
1737: */
1738: function next_newmoon($year, $month, $day, $hour=0.0, $min=0.0, $sec=0.0, $tdiff=NULL) {
1739: //世界時との時差
1740: if ($tdiff == NULL) {
1741: $tdiff = $this->TDIFF;
1742: }
1743:
1744: $dif = 29.55; //一朔望月(近似計算の指標)
1745: $jd = $this->Gregorian2JD($year, $month, $day, $hour, $min, $sec, $tdiff);
1746: $jdc = (float)$jd;
1747:
1748: //ニュートン法による近似計算
1749: for ($i = 1; $i < 20; $i++) {
1750: list($year, $month, $day, $hour, $min, $sec) = $this->JD2Gregorian($jdc, $tdiff);
1751: $ym = $this->longitude_moon($year, $month, $day, $hour, $min, $sec, $tdiff);
1752: $ys = $this->longitude_sun($year, $month, $day, $hour, $min, $sec, $tdiff);
1753:
1754: //太陽と月の黄緯差0度を目指す.
1755: $dy = $ym - $ys;
1756:
1757: //引き込み範囲に入ったら補正する.
1758: if ($dy <= -360.0) {
1759: $dy += 360.0;
1760: } else if ($dy >= 0.0) {
1761: $dy -= 360.0;
1762: }
1763:
1764: //誤差内に入ったらループを脱出する.
1765: if ($dy >= 0.0 && $dy <= 0.00001) break;
1766: if ($dy <= 0.0 && $dy >= -0.00001) break;
1767:
1768: $dd = $dy / $dif;
1769: $jdc -= $dd;
1770: }
1771:
1772: return array($year, $month, $day, $hour, $min, $sec);
1773: }
月の視黄経から朔の日時を求める逆関数は存在しないため、ニュートンの近似法を使って求めることにする。無限ループに陥ることを避けるため、ループ回数は20回に制限している。
解説:月齢の算出
1775: /**
1776: * 指定した日時における月齢を求める(日時はローカル時間).
1777: * 参考サイト https://www.pahoo.org/e-soul/webtech/php02/php02-43-01.shtm
1778: * @param int $year, $month, $day 年月日(グレゴリオ暦)
1779: * @param float $hour, $min, $sec 時分秒(ローカル時間)
1780: * @param float $tdiff UTCとの時差;NULLの時はTDIFF
1781: * @return float 月齢
1782: */
1783: function moon_age($year, $month, $day, $hour, $min, $sec, $tdiff=NULL) {
1784: //世界時との時差
1785: if ($tdiff == NULL) {
1786: $tdiff = $this->TDIFF;
1787: }
1788: //一朔望月を遡って,次の朔の日時を求める.
1789: $jd0 = $this->Gregorian2JD($year, $month, $day, $hour, $min, $sec, $tdiff);
1790: $jd1 = $jd0 - 29.55;
1791: list($year, $month, $day, $hour, $min, $sec) = $this->JD2Gregorian($jd1, $tdiff);
1792: list($year, $month, $day, $hour, $min, $sec) = $this->next_newmoon($year, $month, $day, $hour, $min, $sec, $tdiff);
1793: $jd2 = $this->Gregorian2JD($year, $month, $day, $hour, $min, $sec, $tdiff);
1794:
1795: //次の朔望月になっていた場合,基準日時を変えて再計算する.
1796: if ($jd2 >= $jd0) {
1797: $jd2 -= 29.55 * 2;
1798: list($year, $month, $day, $hour, $min, $sec) = $this->JD2Gregorian($jd2, $tdiff);
1799: list($year, $month, $day, $hour, $min, $sec) = $this->next_newmoon($year, $month, $day, $hour, $min, $sec, $tdiff);
1800: $jd2 = $this->Gregorian2JD($year, $month, $day, $hour, $min, $sec, $tdiff);
1801: }
1802:
1803: //月齢を算出する.
1804: return $jd0 - $jd2;
1805: }
ここから、月齢を求めるためには、「PHPで二十四節気一覧を作成」で紹介した太陽の視黄経と、今回作成した 月の視黄経の差分を計算することにする。
指定した日時より29.55日前(一朔望月=新月から次の新月までの日数)を減じて、その時点から次の朔を next_newmoon を使って求める。
この朔の日時と指定日時の差を求めれば月齢になるはずだが、内部演算誤差で指定日時より一朔望月未来の朔の比を算出する恐れがある。そこで、差がマイナスになった場合には、基準日時を変えて、再度、月齢を計算する。
活用例
参考サイト
- PHPでスーパームーンを計算する:ぱふぅ家のホームページ
- PHPで二十四節気一覧を作成:ぱふぅ家のホームページ
- 月齢=今日の月・現在の月=:みんなの知識 ちょっと便利帳
- こよみ用語解説 二十四節気:国立天文台暦計算室
- CalcMoonage:GitHub
- Ruby - 月齢計算!:mk-mode BLOG
月齢は、太陽と月の位置関係から計算できる。今回は、PHPで月の視黄経を算出するプログラムを作り、指定した月から3ヶ月分の月齢一覧を求めることを目標にする。
(2023年1月14日)計算精度を向上した。calcMoonAge()で時刻指定可能にした。数値入力にSpinner、pahooInputDataを導入した。