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

サンプル・プログラム
tripleCalendar.php | サンプル・プログラム本体。 |
pahooCalendar.php | 暦計算クラス pahooCalendar。 暦計算クラスの使い方は「PHPで日出没・月出没・月齢・潮を計算」を参照。include_path が通ったディレクトリに配置すること。 |
解説:旧暦の計算
1777: /**
1778: * グレゴオリオ暦=旧暦テーブル 作成
1779: * @param int $year 西暦年
1780: * @return double 太陽の黄経(視黄経)
1781: */
1782: function makeLunarCalendar($year) {
1783: //旧暦の2033年問題により、2033年以降はエラーフラグを立てる
1784: if ($year >= 2033) {
1785: $this->error = TRUE;
1786: $this->errmsg = '2033年以降は正しい旧暦計算ができません';
1787: }
1788:
1789: unset($this->tblmoon);
1790: $this->tblmoon = array();
1791:
1792: //前年の冬至を求める
1793: for ($day = 1; $day <= 31; $day++) {
1794: $lsun = $this->longitude_sun($year - 1, 12, $day, 0, 0, 0);
1795: if (floor($lsun / 15.0) > 17) break;
1796: }
1797: $d1 = $day - 1; //冬至
1798:
1799: //翌年の雨水を求める
1800: for ($day = 1; $day <= 31; $day++) {
1801: $lsun = $this->longitude_sun($year + 1, 2, $day, 0, 0, 0);
1802: if (floor($lsun / 15.0) > 22) break;
1803: }
1804: $d2 = $day - 1; //雨水
1805:
1806: //朔の日を求める
1807: $cnt = 0;
1808: $dd = $d1;
1809: $mm = 12;
1810: $yy = $year - 1;
1811: while ($yy <= $year + 1) {
1812: $dm = $this->getDaysInMonth($yy, $mm);
1813: while ($dd <= $dm) {
1814: $age1 = $this->moon_age($yy, $mm, $dd, 0 - $this->TDIFF, 0, 0); //Ver.3.11 bug-fix
1815: $age2 = $this->moon_age($yy, $mm, $dd, 23 - $this->TDIFF, 59, 59); //Ver.3.11 bug-fix
1816: if ($age2 <= $age1) {
1817: $this->tblmoon[$cnt]['year'] = $yy;
1818: $this->tblmoon[$cnt]['month'] = $mm;
1819: $this->tblmoon[$cnt]['day'] = $dd;
1820: $this->tblmoon[$cnt]['age'] = $age1;
1821: $this->tblmoon[$cnt]['jd'] = $this->Gregorian2JD($yy, $mm, $dd, 0, 0, 0);
1822: $cnt++;
1823: }
1824: $dd++;
1825: }
1826: $mm++;
1827: $dd = 1;
1828: if ($mm > 12) {
1829: $yy++;
1830: $mm = 1;
1831: }
1832: }
1833:
1834: //二十四節気(中)を求める
1835: $tblsun = array();
1836: $cnt = 0;
1837: $dd = $d1;
1838: $mm = 12;
1839: $yy = $year - 1;
1840: while ($yy <= $year + 1) {
1841: $dm = $this->getDaysInMonth($yy, $mm);
1842: while ($dd <= $dm) {
1843: $l1 = $this->longitude_sun($yy, $mm, $dd, 0, 0, 0);
1844: $l2 = $this->longitude_sun($yy, $mm, $dd, 24, 0, 0);
1845: $n1 = floor($l1 / 15.0);
1846: $n2 = floor($l2 / 15.0);
1847: if (($n2 != $n1) && ($n2 % 2 == 0)) {
1848: $tblsun[$cnt]['jd'] = $this->Gregorian2JD($yy, $mm, $dd, 0, 0, 0);
1849: $oldmonth = floor($n2 / 2) + 2;
1850: if ($oldmonth > 12) $oldmonth -= 12;
1851: $tblsun[$cnt]['oldmonth'] = $oldmonth;
1852: $cnt++;
1853: }
1854: $dd++;
1855: }
1856: $mm++;
1857: $dd = 1;
1858: if ($mm > 12) {
1859: $yy++;
1860: $mm = 1;
1861: }
1862: }
1863:
1864: //月の名前を決める
1865: $n1 = count($this->tblmoon);
1866: $n2 = count($tblsun);
1867: for ($i = 0; $i < $n1 - 1; $i++) {
1868: for ($j = 0; $j < $n2; $j++) {
1869: if (($this->tblmoon[$i]['jd'] <= $tblsun[$j]['jd'])
1870: && ($this->tblmoon[$i + 1]['jd'] > $tblsun[$j]['jd'])) {
1871: $this->tblmoon[$i]['oldmonth'] = $tblsun[$j]['oldmonth'];
1872: $this->tblmoon[$i]['oldleap'] = FALSE;
1873: $this->tblmoon[$i + 1]['oldmonth'] = $tblsun[$j]['oldmonth'];
1874: $this->tblmoon[$i + 1]['oldleap'] = TRUE;
1875: break;
1876: }
1877: }
1878: }
1879: }

変換のための方程式を導出できないため、あらかじめ計算したい西暦年から変換テーブルを作る必要がある。これを行うのがユーザー関数 makeLunarCalendar である。
- 前年の冬至を求める。
- 翌年の雨水を求める。
- 1~2の期間中の二十四節気(中)を配列 $tblsun に格納する。
- 朔日と中を比較して、月の名前と閏月を決めてゆく。
1881: /**
1882: * 旧暦を求める
1883: * @param int $year 西暦年
1884: * @param int $month 月
1885: * @param int $day 日
1886: * @return array(旧暦月,日,閏月フラグ)/FALSE:旧暦計算不能
1887: */
1888: function Gregorian2Lunar($year, $month, $day) {
1889: //2033年問題チェック
1890: if ($this->error) return FALSE;
1891:
1892: $jd = $this->Gregorian2JD($year, $month, $day, 0, 0, 0);
1893: $str = '';
1894: $n1 = count($this->tblmoon);
1895: for ($i = 0; $i < $n1 - 1; $i++) {
1896: if ($jd < $this->tblmoon[$i + 1]['jd']) {
1897: $day = floor($jd - $this->tblmoon[$i]['jd']) + 1;
1898: $items = array($this->tblmoon[$i]['oldmonth'], $day, $this->tblmoon[$i]['oldleap']);
1899: break;
1900: }
1901: }
1902: return $items;
1903: }

この旧暦計算法は天保暦に基づいているが、天保暦には2033年問題がある。西暦2033年(令和15年)10月は旧暦9月だが、11月に入ると旧暦11月と、10月が無くなってしまう問題である。
これは、1844年(天保14年)に天保暦が導入されてから初めて起きる事態だが、天保暦は1872年(明治5年)に廃止されているために正式な解決法は用意されていない。
そこで、2033年(令和15年)以降の旧暦計算はスキップするようにした。
解説:干支と十二支の計算
1924: /**
1925: * 干支を求める(下請け関数)
1926: * @param int $a1 十干の基準値
1927: * @param int $a2 十二支の基準値
1928: * @param int $n 計算したい値
1929: * @return string 干支
1930: */
1931: function __eto($a1, $a2, $n) {
1932: //十干
1933: static $table1 = array(
1934: 0 =>'甲',
1935: 1 =>'乙',
1936: 2 =>'丙',
1937: 3 =>'丁',
1938: 4 =>'戊',
1939: 5 =>'己',
1940: 6 =>'庚',
1941: 7 =>'辛',
1942: 8 =>'壬',
1943: 9 =>'癸'
1944: );
1945:
1946: //十二支
1947: static $table2 = array(
1948: 0 =>'子',
1949: 1 =>'丑',
1950: 2 =>'寅',
1951: 3 =>'卯',
1952: 4 =>'辰',
1953: 5 =>'巳',
1954: 6 =>'午',
1955: 7 =>'未',
1956: 8 =>'申',
1957: 9 =>'酉',
1958: 10 =>'戌',
1959: 11 =>'亥'
1960: );
1961:
1962: return $table1[abs($n - $a1) % 10] . $table2[abs($n - $a2) % 12];
1963: }
干支は、甲・乙・丙・丁・戊・己・庚・辛・壬・癸の10要素からなる十干と、子・丑・寅・卯・辰・巳・午・未・申・酉・戌・亥の12要素からなる十二支の組み合わせからなる。
つまり、十干は年/月/日が10回ごとに繰り返され、十二支は年/月/日が12回ごとに繰り返されることになる。これを計算するのが __eto である。

次に、基準となる年月日を調べておく。
甲の年は西暦1904年(明治37年)、子の年は西暦1900年(明治33年)である。年の干支は、この2つを基準に計算する。
甲子の月は1903年(明治36年)11月である。月の干支は、ここを基準に計算する。
甲子の日は1902年(明治35年)4月11日である。日の干支は、ここを基準に計算する。

ちなみに、干支も十二支も古代中国で考え出されたものだが、十二支の方は、木星が黄道を1周するのがほぼ12年(公転周期11.86年)ということに対応している。古代中国では木星を暦に関わる重要な天体と位置づけており、歳星という名で呼ばれていた。
解説:土用の判定
0854: /**
0855: * その日が土用かどうか
0856: * @param int $year, $month, $day グレゴリオ暦による年月日
0857: * @return array(季節, 種類)
0858: */
0859: function isDoyo($year, $month, $day) {
0860: static $table1 = array('冬' ,'春', '夏', '秋');
0861: static $table2 = array('in'=>'入り', 'ushi'=>'丑', 'out'=>'明け');
0862: static $year0 = -1; //西暦年保存用
0863: static $doyo = array(); //土用情報保存用
0864:
0865: //土用情報を取得
0866: if ($year0 != $year) {
0867: $year0 = $year;
0868: $doyo = $this->getDoyo($year0);
0869: }
0870:
0871: //土用判定
0872: foreach ($doyo as $key1=>$arr1) {
0873: foreach ($arr1 as $key2=>$arr2) {
0874: if (($arr2['year'] == $year0) && ($arr2['month'] == $month) && ($arr2['day'] == $day)) {
0875: return array($table1[$key1], $table2[$key2]);
0876: }
0877: }
0878: }
0879: return array('', '');
0880: }

isDoyo メソッドは、まず、「PHPで土用を求める」で作った getDoyo メソッドを呼び出し、その年の土用に関する情報をstatic配列 $doyo にストックしておく。計算量を減らす目的で、次に呼び出されたときも同じ年なら、この配列 $doyo を参照する。

引数として与えられた年月日が土用の入り、明け、丑の日であるかどうか、この配列 $doyo をスキャンして判定する。
解説:彼岸の計算
0882: /**
0883: * その年の彼岸を求める
0884: * @param int $year 西暦年
0885: * @return array [0]['in']['year','month','day'] 春の彼岸入り
0886: * ['out']['year','month','day'] 春の彼岸明け
0887: * [1] 秋の土用の入り/明け
0888: */
0889: function getHigan($year) {
0890: static $higan = array(); //彼岸情報保存用
0891:
0892: //春の彼岸
0893: $day = $this->getVernalEquinox($year);
0894: $higan[0]['in']['year'] = (int)$year;
0895: $higan[0]['in']['month'] = (int)3;
0896: $higan[0]['in']['day'] = (int)($day - 3);
0897: $higan[0]['out']['year'] = (int)$year;
0898: $higan[0]['out']['month'] = (int)3;
0899: $higan[0]['out']['day'] = (int)($day + 3);
0900:
0901: //秋の彼岸
0902: $day = $this->getAutumnalEquinox($year);
0903: $higan[1]['in']['year'] = (int)$year;
0904: $higan[1]['in']['month'] = (int)9;
0905: $higan[1]['in']['day'] = (int)($day - 3);
0906: $higan[1]['out']['year'] = (int)$year;
0907: $higan[1]['out']['month'] = (int)9;
0908: $higan[1]['out']['day'] = (int)($day + 3);
0909:
0910: return $higan;
0911: }

彼岸入り、明けの求め方については、その年の彼岸を求める getHigan メソッドを用意した。カレンダー計算の冒頭で実行する。
まず、getVernalEquinox メソッドで春分の日を求め、そこから3日前を彼岸入りに、3日後を彼岸明けとして登録する。
秋の彼岸については、getAutumnalEquinox メソッドで秋分の日を求め、同様に登録する。
解説:雑節の計算
0913: /**
0914: * その年の雑節を求める
0915: * @param int $year 西暦年
0916: * @return array [雑節]['year','month','day']
0917: * 雑節‥‥社日,八十八夜,入梅,二百十日,二百二十日
0918: * 次の雑節は個別のメソッドを利用する
0919: * 節分→ isSetsubun()
0920: * 彼岸→ getHigan()
0921: * 半夏生→ getSolarTerm72()
0922: * 土用→isDoyo()
0923: */
0924: function getZassetsu($year) {
0925: static $zassetsu = array(); //雑節情報保存用
0926:
0927: //社日計算テーブル
0928: static $table_sha = array(
0929: '甲' => +4,
0930: '乙' => +3,
0931: '丙' => +2,
0932: '丁' => +1,
0933: '戊' => 0,
0934: '己' => -1,
0935: '庚' => -2,
0936: '辛' => -3,
0937: '壬' => -4,
0938: );
0939:
0940: //春社を求める
0941: $label = '春社';
0942: $day = $this->getVernalEquinox($year); //春分の日
0943: $month = 3;
0944:
0945: $eto = $this->eto_day($year, $month, $day);
0946: //癸の日の場合
0947: if (preg_match('/癸/ui', $eto) > 0) {
0948: $zassetsu[$label]['year'] = $year;
0949: $zassetsu[$label]['month'] = $month;
0950: $l1 = $this->longitude_sun($year, $month, $day, 12, 0, 0);
0951: //春分が午前中なら前の戊の日
0952: if (($l1 < 0) || ($l1 >= 360)) {
0953: $zassetsu[$label]['day'] = $day - 5;
0954: //春分が午後なら前の戊の日
0955: } else {
0956: $zassetsu[$label]['day'] = $day + 5;
0957: }
0958: //それ以外の場合
0959: } else {
0960: foreach ($table_sha as $key=>$val) {
0961: if (preg_match('/' . $key . '/ui', $eto) > 0) {
0962: $zassetsu[$label]['year'] = $year;
0963: $zassetsu[$label]['month'] = $month;
0964: $zassetsu[$label]['day'] = $day + $val;
0965: break;
0966: }
0967: }
0968: }
0969:
0970: //秋社を求める
0971: $label = '秋社';
0972: $day = $this->getAutumnalEquinox($year); //秋分の日
0973: $month = 9;
0974: $eto = $this->eto_day($year, $month, $day);
0975: //癸の日の場合
0976: if (preg_match('/癸/ui', $eto) > 0) {
0977: $zassetsu[$label]['year'] = $year;
0978: $zassetsu[$label]['month'] = $month;
0979: $l1 = $this->longitude_sun($year, $month, $day, 12, 0, 0);
0980: //秋分が午前中なら前の戊の日
0981: if ($l1 <= 180) {
0982: $zassetsu[$label]['day'] = $day - 5;
0983: //秋分が午後なら前の戊の日
0984: } else {
0985: $zassetsu[$label]['day'] = $day + 5;
0986: }
0987: //それ以外の場合
0988: } else {
0989: foreach ($table_sha as $key=>$val) {
0990: if (preg_match('/' . $key . '/ui', $eto) > 0) {
0991: $zassetsu[$label]['year'] = $year;
0992: $zassetsu[$label]['month'] = $month;
0993: $zassetsu[$label]['day'] = $day + $val;
0994: break;
0995: }
0996: }
0997: }
0998:
0999: //立春を求める
1000: $month = 2;
1001: $day = 1;
1002: while ($day < 10) {
1003: if ($this->getSolarTerm($year, $month, $day) == '立春') break;
1004: $day++;
1005: }
1006: $ve = $this->AD2JD($year, $month, $day, 0, 0, 0);
1007:
1008: //八十八夜
1009: $label = '八十八夜';
1010: list($zassetsu[$label]['year'], $zassetsu[$label]['month'], $zassetsu[$label]['day']) = $this->JD2Gregorian($ve + 87);
1011:
1012: //入梅
1013: $label = '入梅';
1014: $month = 6;
1015: $day = 1;
1016: while ($day < 30) {
1017: //太陽黄経
1018: $l2 = $this->longitude_sun($year, $month, $day, 24, 0, 0);
1019: if ($l2 >= 80) {
1020: $zassetsu[$label]['year'] = $year;
1021: $zassetsu[$label]['month'] = $month;
1022: $zassetsu[$label]['day'] = $day;
1023: break;
1024: }
1025: $day++;
1026: }
1027:
1028: //二百十日
1029: $label = '二百十日';
1030: list($zassetsu[$label]['year'], $zassetsu[$label]['month'], $zassetsu[$label]['day']) = $this->JD2Gregorian($ve + 209);
1031:
1032: //二百二十日
1033: $label = '二百二十日';
1034: list($zassetsu[$label]['year'], $zassetsu[$label]['month'], $zassetsu[$label]['day']) = $this->JD2Gregorian($ve + 219);
1035:
1036: return $zassetsu;
1037: }
ここでは、残る社日、八十八夜、入梅、二百十日、二百二十日の求め方を紹介する。

社日は、産土神を祀る日で、春・秋の2回ある。春のものを春社、秋のものを秋社と呼ぶ。
春分に最も近い戊の日が社日となる。ただし戊と戊のちょうど中間、つまり癸の日が秋分の日に重なる場合は、春分の瞬間が午前中ならば前の戊の日、午後ならば後の戊の日とする。
秋分の日についても同様にして求める。

入梅は太陽黄経が80度の日である。

立春を第1日目として、八十八夜は88日目、二百十日は210日目、二百二十日は220日目を、それぞれ指す。

これらの雑節を求めるのが getZassetsu メソッドである。カレンダー計算の冒頭で実行する。
上記で説明した雑節の求め方をプログラムに実装した。
解説:一粒万倍日
1685年(貞享2年)に渋川春海が編纂した貞享暦から外されたが、近年、宝くじを買うのに良い日と宣伝され、再び脚光を浴びるようになった。

一粒万倍日は、節月と日の干支の組み合わせで決まる。
組み合わせ(計算方式)は2つあり、下表に整理する。
節月 | 二十四節気 | 計算方式I | 計算方式II |
---|---|---|---|
1 | 立春~啓蟄 | 丑・午 | 酉 |
2 | 啓蟄~清明 | 酉・寅 | 申 |
3 | 清明~立夏 | 子・卯 | 未 |
4 | 立夏~芒種 | 卯・辰 | 午 |
5 | 芒種~小暑 | 巳・午 | 巳 |
6 | 小暑~立秋 | 酉・午 | 辰 |
7 | 立秋~白露 | 子・未 | 卯 |
8 | 白露~寒露 | 卯・申 | 寅 |
9 | 寒露~立冬 | 酉・午 | 丑 |
10 | 立冬~大雪 | 酉・戌 | 子 |
11 | 大雪~小寒 | 亥・子 | 亥 |
12 | 小寒~立春 | 卯・子 | 戌 |
0631: /**
0632: * その日の節月を求める
0633: * @param int $year, $month, $day グレゴリオ暦による年月日
0634: * @return int 節月/0:計算失敗
0635: */
0636: function getSetsugetsu($year, $month, $day) {
0637: static $table = array(
0638: 1 => 315,
0639: 2 => 345,
0640: 3 => 15,
0641: 4 => 45,
0642: 5 => 75,
0643: 6 => 105,
0644: 7 => 135,
0645: 8 => 165,
0646: 9 => 195,
0647: 10 => 225,
0648: 11 => 255,
0649: 12 => 285,
0650: 13 => 315
0651: );
0652:
0653: //太陽黄経
0654: $ls = $this->longitude_sun($year, $month, $day, 24, 0, 0);
0655:
0656: //節月
0657: foreach ($table as $key=>$val) {
0658: if ($key == 2) {
0659: if (($ls >= $table[2]) || ($ls < $table[3])) return $key;
0660: } else {
0661: if (($ls >= $table[$key]) && ($ls < $table[$key + 1])) return $key;
0662: }
0663: }
0664: return 0;
0665: }
1998: /**
1999: * 一粒万倍日かどうか
2000: * @param int $year, $month, $day グレゴリオ暦による年月日
2001: * @param int $method 計算方式(1または2)(省略可能:デフォルトは1)
2002: * @return bool TRUE:一粒万倍日/FALSE:ではない
2003: */
2004: function ichiryumanbai($year, $month, $day, $method=1) {
2005: //計算方式I
2006: $table[1] = array(
2007: 1 => array('丑', '午'),
2008: 2 => array('酉', '寅'),
2009: 3 => array('子', '卯'),
2010: 4 => array('卯', '辰'),
2011: 5 => array('巳', '午'),
2012: 6 => array('酉', '午'),
2013: 7 => array('子', '未'),
2014: 8 => array('卯', '申'),
2015: 9 => array('酉', '午'),
2016: 10 => array('酉', '戌'),
2017: 11 => array('亥', '子'),
2018: 12 => array('卯', '子'),
2019: );
2020: //計算方式II
2021: $table[2] = array(
2022: 1 => array('酉'),
2023: 2 => array('申'),
2024: 3 => array('未'),
2025: 4 => array('午'),
2026: 5 => array('巳'),
2027: 6 => array('辰'),
2028: 7 => array('卯'),
2029: 8 => array('寅'),
2030: 9 => array('丑'),
2031: 10 => array('子'),
2032: 11 => array('亥'),
2033: 12 => array('戌'),
2034: );
2035:
2036: //計算方式のチェック
2037: if (($method != 1) && ($method != 2)) return FALSE;
2038:
2039: //節月を求める
2040: $setsu = $this->getSetsugetsu($year, $month, $day);
2041: //干支の右1文字取得
2042: $eto = mb_substr($this->eto_day($year, $month, $day), 1, 1);
2043:
2044: return in_array($eto, $table[$method][$setsu]);
2045: }
解説:カレンダー作成
0187: /**
0188: * 1ヶ月分のカレンダーを作成
0189: * @param object $pcl pahooCalendarオブジェクト
0190: * @param int $start 週の開始曜日(0:日曜日, 1:月曜日...6:土曜日)
0191: * @param int $year 西暦年
0192: * @param int $month 月
0193: * @return string HTMLコンテンツ/FALSE:エラー
0194: */
0195: function makeCalendar($pcl, $start, $year, $month) {
0196: //雑節テーブル
0197: $table_za = array('春社', '秋社', '八十八夜', '入梅', '二百十日', '二百二十日');
0198:
0199: //月のエラーチェック
0200: if ($month < 1 && $month > 12) return FALSE;
0201:
0202: $eto_year = $pcl->eto_year($year);
0203: $eto_month = $pcl->eto_month($year, $month);
0204:
0205: //彼岸
0206: $higan = $pcl->getHigan($year);
0207: //雑節
0208: $zassetsu = $pcl->getZassetsu($year);
0209:
0210: $html =<<< EOT
0211: <table class="calendar">
0212: <tr>
0213: <th colspan="7"><span class="large">{$year}年({$eto_year}) {$month}月({$eto_month})</span></th>
0214: </tr>
0215: <tr>
0216:
0217: EOT;
0218:
0219: //曜日の行
0220: for ($i = 0; $i < 7; $i++) {
0221: $n = ($start + $i) % 7;
0222: if ($n == 6) $class = 'blue';
0223: else if ($n == 0) $class = 'red';
0224: else $class = 'black';
0225: $str = $pcl->__getWeekString($n);
0226: $html .= "<th><span class=\"{$class}\">{$str}</span></th>";
0227: }
0228: $html .= "</tr>\n";
0229:
0230: //カレンダー本体
0231: $wn1 = $pcl->getWeekNumber($year, $month, 1); //月の最初の曜日
0232: $dim = $pcl->getDaysInMonth($year, $month); //月の最後の日
0233: $cnt = 0;
0234: $flag = FALSE;
0235: $n = $start;
0236: $day = 1;
0237: while (1) {
0238: if ($cnt % 7 == 0) $html .= "<tr>\n";
0239: //曜日の色
0240: if ($n % 7 == 6) $class = 'blue';
0241: else if ($n % 7 == 0) $class = 'red';
0242: else $class = 'black';
0243: if ($n % 7 == $wn1) $flag = TRUE;
0244: //表示開始
0245: if ($flag) {
0246: $ss = '';
0247: //祝日
0248: $holiday = $pcl->getHoliday($year, $month, $day);
0249: if ($holiday != '') {
0250: $ss .= "<span class=\"small red\">{$holiday}<br /></span>\n";
0251: }
0252: //二十四節気
0253: $solarterm = $pcl->getSolarTerm($year, $month, $day);
0254: //土用
0255: list($ss1, $ss2) = $pcl->isDoyo($year, $month, $day);
0256: if ($ss2 == '明け') {
0257: $ss2 = '';
0258: } else if (($ss1 != '夏') && ($ss2 == '丑')) {
0259: $ss2 = '';
0260: }
0261: if ($ss1 != '' && $ss2 != '') {
0262: if ($solarterm != '') {
0263: $solarterm .= '<br />土用の' . $ss2;
0264: } else {
0265: $solarterm = '土用の' . $ss2;
0266: }
0267: }
0268: if ($solarterm != '') {
0269: $ss .= "<span class=\"small\">{$solarterm}<br /></span>\n";
0270: }
0271: //七十二候
0272: $solarterm72 = $pcl->getSolarTerm72($year, $month, $day);
0273: if ($solarterm72 != '') {
0274: $ss .= "<span class=\"small\">{$solarterm72}<br /></span>\n";
0275: }
0276: //彼岸
0277: if (($higan[0]['in']['month'] == $month) && ($higan[0]['in']['day'] == $day)) {
0278: $solarterm = '彼岸入り';
0279: $ss .= "<span class=\"small\">彼岸入り<br /></span>\n";
0280: } else if (($higan[0]['out']['month'] == $month) && ($higan[0]['out']['day'] == $day)) {
0281: $solarterm = '彼岸明け';
0282: $ss .= "<span class=\"small\">彼岸明け<br /></span>\n";
0283: } else if (($higan[1]['in']['month'] == $month) && ($higan[1]['in']['day'] == $day)) {
0284: $solarterm = '彼岸入り';
0285: $ss .= "<span class=\"small\">彼岸入り<br /></span>\n";
0286: } else if (($higan[1]['out']['month'] == $month) && ($higan[1]['out']['day'] == $day)) {
0287: $solarterm = '彼岸明け';
0288: $ss .= "<span class=\"small\">彼岸明け<br /></span>\n";
0289: }
0290: //雑節
0291: foreach ($table_za as $label) {
0292: if (($zassetsu[$label]['month'] == $month) && ($zassetsu[$label]['day'] == $day)) {
0293: $solarterm = $label;
0294: $ss .= "<span class=\"small\">{$label}<br /></span>\n";
0295: }
0296: }
0297: //一粒万倍日
0298: $manbai = $pcl->ichiryumanbai($year, $month, $day, 1) ? '一粒万倍日' : '';
0299: if ($manbai != '') {
0300: $ss .= "<span class=\"small\">{$manbai}<br /></span>\n";
0301: }
0302: //旧暦
0303: list($oldmonth, $oldday, $oldleap) = $pcl->Gregorian2Lunar($year, $month, $day);
0304: if (!$pcl->error) {
0305: $oldleap = $oldleap ? '閏' : '';
0306: $rokuyou = $pcl->rokuyou($oldmonth, $oldday);
0307: $oldcal = sprintf('%s%d月%d日<br />(%s)', $oldleap, $oldmonth, $oldday, $rokuyou);
0308: //2033年問題回避
0309: } else {
0310: $oldcal = '';
0311: }
0312: //日の干支
0313: $eto_day = $pcl->eto_day($year, $month, $day);
0314:
0315: //表示
0316: $html .=<<< EOT
0317: <td>
0318: <span class="large {$class}">{$day}</span>
0319: <div class="narrow">
0320: <span class="small red">{$holiday}</span><br />
0321: <span class="small blue">{$manbai}</span><br />
0322: <span class="small">{$solarterm}</span><br />
0323: <span class="small">{$solarterm72}</span><br />
0324: <span class="small">{$eto_day}</span><br />
0325: <span class="small">{$oldcal}</span>
0326: </div>
0327: </td>
0328:
0329: EOT;
0330: $day++;
0331: if ($day > $dim) break;
0332: } else {
0333: $html .= "<td> </td>";
0334: }
0335: $cnt++;
0336: $n++;
0337: if ($cnt % 7 == 0) $html .= "</tr>\n";
0338: }
0339: //最後の日以降の処理
0340: $cnt++;
0341: while ($cnt % 7 != 0) {
0342: $html .= "<td> </td>";
0343: $cnt++;
0344: if ($cnt % 7 == 0) $html .= "</tr>\n";
0345: }
0346:
0347: $html .= "</table>\n";
0348:
0349: return $html;
0350: }
週の開始曜日を自由に設定できるようにするための工夫をしている。

カレンダー本体を作成する処理では、まず、月の最初の曜日 $wn1 と、月の最後の日 $dim を計算しておく。
$wn1 と週の開始曜日 $start が一致するまでは変数 $flag がFALSEのままで、この時は日付を入れずに空のままにしておく。
$flag がTRUEになったら日付を入れてゆく。あわせて、二十四節気、旧暦の計算を行う。
土用の判定については、四季の土用の入りと丑の日のみを表示するようにしている。土用の明けは、立春、立夏、立秋、立冬の前日であるから、二十四節気の表示からわかるからだ。
$dim に達したらループを抜け出し、行末まで空白のセルを追加してゆく。
質疑応答
「PHPで3ヶ月カレンダーを作る」のページのソースなのですが「サンプル・プログラムの解説:カレンダー作成」のソースの0171行目に「<tr>」の開始タグを入れたほうがいいと思います。曜日部分の行部分TRの開始タグが無いようです。【回答】
間違えであればすいません。
ご指摘ありがとうございます。<tr> タグが抜けていました。追加しました。
活用例
参考サイト
- PHPで祝日を求める:ぱふぅ家のホームページ
- PHPで二十四節気一覧を作成:ぱふぅ家のホームページ
- PHPで土用を求める:ぱふぅ家のホームページ
- 旧暦と六曜を作りましょう:こよみのページ
- 七十二候:今日は何の日~毎日が記念日~
- 旧暦2033年問題:Oka Laboratory 備忘録
- 旧暦六曜Javaソースプログラム公開:がらくた研究室
- 「月の名前」の計算方法:ここですという名前のブログやさん
- 二十四節気・七十二候・干支・旧暦・六曜カレンダー:みんなの知識 ちょっと便利帳
- 歴代天皇の誕生日が祝日に!?:みんなの知識 ちょっと便利帳
- 一粒万倍日を調べる〈タイプ1〉:みんなの知識 ちょっと便利帳
参考書籍
![]() |
知れば知るほど面白い暦の謎 | ||
著者 | 片山 真人 | ||
出版社 | 三笠書房 | ||
サイズ | 文庫 | ||
発売日 | 2022年02月17日頃 | ||
価格 | 858円(税込) | ||
ISBN | 9784837987635 | ||
1週間はなぜ「7日」なのか?「曜日」はどのように生まれたか?国立天文台「暦の専門家」が教える! | |||
「PHPで祝日を求める」「PHPで二十四節気一覧を作成」に、雑節や七十二候、旧暦、六曜、干支、一粒万倍日の計算を加えた。
(2022年5月1日)彼岸,社日,八十八夜,入梅,二百十日,二百二十日を追加
(2022年4月9日)一粒万倍日の基準となる節月の計算で、境界条件を修正
(2022年4月2日)一粒万倍日の基準となる節月の計算で、太陽黄経の小数点以下を切り上げるようにした
(2022年3月28日)一粒万倍日を追加,表示改良
(2021年8月8日)土用の入り,丑の日を追加
(2021年5月8日)PHP8対応,リファラ・チェック改良