$everymonth[年索引][1..13] 存儲每月天數(29/30),第 0 元素標記閏月位置(0=無閏月),第 14/15 元素存儲該年干支索引。
$everymonth = [
0 => [8,0,0,0,0,0,0,0,0,0,0,0, 29,30, 7,1], // 1900年:閏8月,年干=7(庚)、支=1(子)
1 => [0,29,30,29,29,30,29,30,29,30,30,30, 29,0, 8,2], // 1901年:無閏月
// ... 共 121 筆 (1900-2020)
];
$total = 11; // 1900-12-21 到 1900-12-31 共 11 天
for ($y=1901; $y<$year; $y++) {
$total += 365;
if ($y%4==0) $total++; // 閏年
}
$monthDays = [31,28,31,30,31,30,31,31,30,31,30,31];
if ($year%4==0) $monthDays[1]=29;
for ($m=1; $m<$month; $m++) $total += $monthDays[$m-1];
$mtotal = 0;
for ($j=0; $j<=120; $j++) {
for ($i=1; $i<=13; $i++) {
$mtotal += $everymonth[$j][$i];
if ($mtotal >= $total) { $foundYear=$j; $foundMonth=$i; break 2; }
}
}
// 當月 1 日對應農曆日期
$md = $everymonth[$foundYear][$foundMonth] - ($mtotal - $total);
$mten = ['','甲','乙','丙','丁','戊','己','庚','辛','壬','癸'];
$mtwelve = ['','子(鼠)','丑(牛)','寅(虎)','卯(兔)','辰(龍)','巳(蛇)','午(馬)','未(羊)','申(猴)','酉(雞)','戌(狗)','亥(豬)'];
$yearGan = $mten[$everymonth[$foundYear][14]];
$yearZhi = $mtwelve[$everymonth[$foundYear][15]];
<?php
// === 配置 ===
$file = $_SERVER['PHP_SELF'];
$year = isset($_GET['year']) ? (int)$_GET['year'] : (int)date('Y');
$month = isset($_GET['month']) ? (int)$_GET['month'] : (int)date('n');
if ($year < 1901 || $year > 2020 || $month < 1 || $month > 12) {
$year = date('Y'); $month = date('n');
}
// === 農曆數據表 (1900-2020) ===
$everymonth = [
0=>[8,0,0,0,0,0,0,0,0,0,0,0,29,30,7,1], 1=>[0,29,30,29,29,30,29,30,29,30,30,30,29,0,8,2],
2=>[0,30,29,30,29,29,30,29,30,29,30,30,30,0,9,3], 3=>[5,29,30,29,30,29,29,30,29,29,30,30,29,30,10,4],
4=>[0,30,30,29,30,29,29,30,29,29,30,30,29,0,1,5], 5=>[0,30,30,29,30,30,29,29,30,29,30,29,30,0,2,6],
6=>[4,29,30,30,29,30,29,30,29,30,29,30,29,30,3,7], 7=>[0,29,30,29,30,29,30,30,29,30,29,30,29,0,4,8],
8=>[0,30,29,29,30,30,29,30,29,30,30,29,30,0,5,9], 9=>[2,29,30,29,29,30,29,30,29,30,30,30,29,30,6,10],
10=>[0,29,30,29,29,30,29,30,29,30,30,30,29,0,7,11], 11=>[6,30,29,30,29,29,30,29,29,30,30,29,30,30,8,12],
12=>[0,30,29,30,29,29,30,29,29,30,30,29,30,0,9,1], 13=>[0,30,30,29,30,29,29,30,29,29,30,29,30,0,10,2],
14=>[5,30,30,29,30,29,30,29,30,29,30,29,29,30,1,3], 15=>[0,30,29,30,30,29,30,29,30,29,30,29,30,0,2,4],
16=>[0,29,30,29,30,29,30,30,29,30,29,30,29,0,3,5], 17=>[2,30,29,29,30,29,30,30,29,30,30,29,30,29,4,6],
18=>[0,30,29,29,30,29,30,29,30,30,29,30,30,0,5,7], 19=>[7,29,30,29,29,30,29,29,30,30,29,30,30,30,6,8],
20=>[0,29,30,29,29,30,29,29,30,30,29,30,30,0,7,9], 21=>[0,30,29,30,29,29,30,29,29,30,29,30,30,0,8,10],
22=>[5,30,29,30,30,29,29,30,29,29,30,29,30,30,9,11], 23=>[0,29,30,30,29,30,29,30,29,29,30,29,30,0,10,12],
24=>[0,29,30,30,29,30,30,29,30,29,30,29,29,0,1,1], 25=>[4,30,29,30,29,30,30,29,30,30,29,30,29,30,2,2],
26=>[0,29,29,30,29,30,29,30,30,29,30,30,29,0,3,3], 27=>[0,30,29,29,30,29,30,29,30,29,30,30,30,0,4,4],
28=>[2,29,30,29,29,30,29,29,30,29,30,30,30,30,5,5], 29=>[0,29,30,29,29,30,29,29,30,29,30,30,30,0,6,6],
30=>[6,29,30,30,29,29,30,29,29,30,29,30,30,29,7,7], 31=>[0,30,30,29,30,30,29,30,29,29,30,29,30,0,8,8],
32=>[0,30,30,30,29,30,29,30,29,29,30,29,30,0,9,9], 33=>[5,29,30,30,29,30,30,29,30,29,30,29,29,30,10,10],
34=>[0,29,30,29,30,30,29,30,29,30,30,29,30,0,1,11], 35=>[0,29,29,30,29,30,29,30,30,29,30,30,29,0,2,12],
36=>[3,30,29,29,30,29,29,30,30,29,30,30,30,29,3,1], 37=>[0,30,29,29,30,29,29,30,29,30,30,30,29,0,4,2],
38=>[7,30,30,29,29,30,29,29,30,29,30,30,29,30,5,3], 39=>[0,30,30,29,29,30,29,29,30,29,30,29,30,0,6,4],
40=>[0,30,30,29,30,29,30,29,29,30,29,30,29,0,7,5], 41=>[6,30,30,29,30,30,29,30,29,29,30,29,30,29,8,6],
42=>[0,30,29,30,30,29,30,29,30,29,30,29,30,0,9,7], 43=>[0,29,30,29,30,29,30,30,29,30,29,30,29,0,10,8],
44=>[4,30,29,30,29,30,29,30,29,30,30,29,30,30,1,9], 45=>[0,29,29,30,29,29,30,29,30,30,30,29,30,0,2,10],
46=>[0,30,29,29,30,29,29,30,29,30,30,29,30,0,3,11], 47=>[2,30,30,29,29,30,29,29,30,29,30,29,30,30,4,12],
48=>[0,30,29,30,29,30,29,29,30,29,30,29,30,0,5,1], 49=>[7,30,29,30,30,29,30,29,29,30,29,30,29,30,6,2],
50=>[0,29,30,30,29,30,30,29,29,30,29,30,29,0,7,3], 51=>[0,30,29,30,30,29,30,29,30,29,30,29,30,0,8,4],
52=>[5,29,30,29,30,29,30,29,30,30,29,30,29,30,9,5], 53=>[0,29,30,29,29,30,30,29,30,30,29,30,29,0,10,6],
54=>[0,30,29,30,29,29,30,29,30,30,29,30,30,0,1,7], 55=>[3,29,30,29,30,29,29,30,29,30,29,30,30,30,2,8],
56=>[0,29,30,29,30,29,29,30,29,30,29,30,30,0,3,9], 57=>[8,30,29,30,29,30,29,29,30,29,30,29,30,29,4,10],
58=>[0,30,30,30,29,30,29,29,30,29,30,29,30,0,5,11], 59=>[0,29,30,30,29,30,29,30,29,30,29,30,29,0,6,12],
60=>[6,30,29,30,29,30,30,29,30,29,30,29,30,29,7,1], 61=>[0,30,29,30,29,30,29,30,30,29,30,29,30,0,8,2],
62=>[0,29,30,29,29,30,29,30,30,29,30,30,29,0,9,3], 63=>[4,30,29,30,29,29,30,29,30,29,30,30,30,29,10,4],
64=>[0,30,29,30,29,29,30,29,30,29,30,30,30,0,1,5], 65=>[0,29,30,29,30,29,29,30,29,29,30,30,29,0,2,6],
66=>[3,30,30,30,29,30,29,29,30,29,29,30,30,29,3,7], 67=>[0,30,30,29,30,30,29,29,30,29,30,29,30,0,4,8],
68=>[7,29,30,29,30,30,29,30,29,30,29,30,29,30,5,9], 69=>[0,29,30,29,30,29,30,30,29,30,29,30,29,0,6,10],
70=>[0,30,29,29,30,29,30,30,29,30,30,29,30,0,7,11], 71=>[5,29,30,29,29,30,29,30,29,30,30,30,29,30,8,12],
72=>[0,29,30,29,29,30,29,30,29,30,30,29,30,0,9,1], 73=>[0,30,29,30,29,29,30,29,29,30,30,29,30,0,10,2],
74=>[4,30,30,29,30,29,29,30,29,29,30,30,29,30,1,3], 75=>[0,30,30,29,30,29,29,30,29,29,30,29,30,0,2,4],
76=>[8,30,30,29,30,29,30,29,30,29,29,30,29,30,3,5], 77=>[0,30,29,30,30,29,30,29,30,29,30,29,29,0,4,6],
78=>[0,30,29,30,30,29,30,30,29,30,29,30,29,0,5,7], 79=>[6,30,29,29,30,29,30,30,29,30,30,29,30,29,6,8],
80=>[0,30,29,29,30,29,30,29,30,30,29,30,30,0,7,9], 81=>[0,29,30,29,29,30,29,29,30,30,29,30,30,0,8,10],
82=>[4,30,29,30,29,29,30,29,29,30,29,30,30,30,9,11], 83=>[0,30,29,30,29,29,30,29,29,30,29,30,30,0,10,12],
84=>[10,30,29,30,30,29,29,30,29,29,30,29,30,30,1,1], 85=>[0,29,30,30,29,30,29,30,29,29,30,29,30,0,2,2],
86=>[0,29,30,30,29,30,30,29,30,29,30,29,29,0,3,3], 87=>[6,30,29,30,29,30,30,29,30,30,29,30,29,29,4,4],
88=>[0,30,29,30,29,30,29,30,30,29,30,30,29,0,5,5], 89=>[0,30,29,29,30,29,29,30,30,29,30,30,30,0,6,6],
90=>[5,29,30,29,29,30,29,29,30,29,30,30,30,30,7,7], 91=>[0,29,30,29,29,30,29,29,30,29,30,30,30,0,8,8],
92=>[0,29,30,30,29,29,30,29,29,30,29,30,30,0,9,9], 93=>[3,29,30,30,29,30,29,30,29,29,30,29,30,29,10,10],
94=>[0,30,30,30,29,30,29,30,29,29,30,29,30,0,1,11], 95=>[8,29,30,30,29,30,29,30,30,29,29,30,29,30,2,12],
96=>[0,29,30,29,30,30,29,30,29,30,30,29,29,0,3,1], 97=>[0,30,29,30,29,30,29,30,30,29,30,30,29,0,4,2],
98=>[5,30,29,29,30,29,29,30,30,29,30,30,29,30,5,3], 99=>[0,30,29,29,30,29,29,30,29,30,30,30,29,0,6,4],
100=>[0,30,30,29,29,30,29,29,30,29,30,30,29,0,7,5], 101=>[4,30,30,29,30,29,30,29,29,30,29,30,29,30,8,6],
102=>[0,30,30,29,30,29,30,29,29,30,29,30,29,0,9,7], 103=>[0,30,30,29,30,30,29,30,29,29,30,29,30,0,10,8],
104=>[2,29,30,29,30,30,29,30,29,30,29,30,29,30,1,9], 105=>[0,29,30,29,30,29,30,30,29,30,29,30,29,0,2,10],
106=>[7,30,29,30,29,30,29,30,29,30,30,29,30,30,3,11], 107=>[0,29,29,30,29,29,30,29,30,30,30,29,30,0,4,12],
108=>[0,30,29,29,30,29,29,30,29,30,30,29,30,0,5,1], 109=>[5,30,30,29,29,30,29,29,30,29,30,29,30,30,6,2],
110=>[0,30,29,30,29,30,29,29,30,29,30,29,30,0,7,3], 111=>[0,30,29,30,30,29,30,29,29,30,29,30,29,0,8,4],
112=>[4,30,29,30,30,29,30,29,30,29,30,29,30,29,9,5], 113=>[0,30,29,30,29,30,30,29,30,29,30,29,30,0,10,6],
114=>[9,29,30,29,30,29,30,29,30,30,29,30,29,30,1,7], 115=>[0,29,30,29,29,30,29,30,30,30,29,30,29,0,2,8],
116=>[0,30,29,30,29,29,30,29,30,30,29,30,30,0,3,9], 117=>[6,29,30,29,30,29,29,30,29,30,29,30,30,30,4,10],
118=>[0,29,30,29,30,29,29,30,29,30,29,30,30,0,5,11], 119=>[0,30,29,30,29,30,29,29,30,29,29,30,30,0,6,12],
120=>[4,29,30,30,30,29,30,29,29,30,29,30,29,30,7,1]
];
// === 輔助陣列 ===
$mten = ['','甲','乙','丙','丁','戊','己','庚','辛','壬','癸'];
$mtwelve = ['','子(鼠)','丑(牛)','寅(虎)','卯(兔)','辰(龍)','巳(蛇)','午(馬)','未(羊)','申(猴)','酉(雞)','戌(狗)','亥(豬)'];
$mmonth = ['閏','正','二','三','四','五','六','七','八','九','十','十一','十二','月'];
$mday = ['','初一','初二','初三','初四','初五','初六','初七','初八','初九','初十',
'十一','十二','十三','十四','十五','十六','十七','十八','十九','二十',
'廿一','廿二','廿三','廿四','廿五','廿六','廿七','廿八','廿九','三十'];
// === 計算陽歷總天數 ===
$total = 11;
for ($y=1901; $y<$year; $y++) { $total += 365; if ($y%4==0) $total++; }
$mdays = [31,28,31,30,31,30,31,31,30,31,30,31];
if ($year%4==0) $mdays[1]=29;
for ($m=1; $m<$month; $m++) $total += $mdays[$m-1];
if ($year%4==0 && $month>2) $total++;
// 當月 1 日星期 (0=日)
$week = ($total + 5) % 7; // 1900-12-21 是周五(5)
// === 計算農曆起始 ===
$mtotal=0; $flag=0; $j=0;
while ($j<=120) {
for ($i=1; $i<=13; $i++) {
$mtotal += $everymonth[$j][$i];
if ($mtotal >= $total) { $flag=1; break; }
}
if ($flag) break; $j++;
}
$md = $everymonth[$j][$i] - ($mtotal - $total); // 當月 1 日農曆日期
$mm = ($everymonth[$j][0] && $i > $everymonth[$j][0]) ? $i-1 : $i; // 農曆月
// 當月天數
$dd = $mdays[$month-1];
// 是否跨農曆年
$ty = (($everymonth[$j][0] && $i==13) || (!$everymonth[$j][0] && $i==12)) && ($mtotal-$total < $dd);
?>
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title><?= $year ?>年<?= $month ?>月 - 萬年曆</title>
<style>
body{font:14px/1.6 sans-serif;max-width:900px;margin:20px auto;padding:0 15px}
table{width:100%;border-collapse:collapse;margin-top:10px}
th,td{width:14.28%;height:90px;border:1px solid #eee;text-align:center;vertical-align:top;padding:5px}
th{background:#f5f5f5;font-weight:600}
.sunday{color:#e53935}.saturday{color:#2e7d32}.today{background:#fff3e0;box-shadow:inset 0 0 0 2px #ff9800}
.lunar{font-size:12px;color:#666;margin-top:2px}
.festival{font-size:11px;color:#e53935}
.nav{text-align:center;margin:15px 0}
select{padding:5px 10px;font-size:14px}
</style>
</head><body>
<h2 style="text-align:center"><?= $year ?>年<?= $month ?>月</h2>
<p style="text-align:center;font-size:16px;font-weight:bold">
<?= $mten[$everymonth[$j][14]] . $mtwelve[$everymonth[$j][15]] ?>年
<?php if ($ty): ?> / <?= $mten[$everymonth[$j+1][14]] . $mtwelve[$everymonth[$j+1][15]] ?>年<?php endif; ?>
</p>
<table>
<tr><th class="sunday">日</th><th>一</th><th>二</th><th>三</th><th>四</th><th>五</th><th class="saturday">六</th></tr>
<?php
$day=1; $line=0; $k=$week; $curY=date('Y'); $curM=date('n'); $curD=date('j');
while ($day <= $dd) {
echo '<tr>';
for ($s=0; $s<7; $s++) {
if ($k!==0 || $day>$dd) { echo '<td></td>'; $k--; continue; }
$cls = [];
if ($s===0) $cls[]='sunday';
if ($s===6) $cls[]='saturday';
if ($year==$curY && $month==$curM && $day==$curD) $cls[]='today';
echo '<td class="'.implode(' ',$cls).'">';
echo '<div style="font-size:18px;font-weight:bold">'.$day.'</div>';
// 農曆
$lunar = ($md==1) ? $mmonth[($everymonth[$j][0] && $i==$everymonth[$j][0]+1)?0:$mm].$mmonth[13] : $mday[$md];
echo '<div class="lunar">'.$lunar.'</div>';
// 節氣/節日可在此擴展
$day++; $md++;
if ($md > $everymonth[$j][$i]) { $md=1; $i++; }
if (($i>12 && !$everymonth[$j][0]) || ($i>13 && $everymonth[$j][0])) { $i=1; $j++; }
}
echo '</tr>'; $line++;
}
// 補足 6 行
for ($l=$line; $l<6; $l++) echo '<tr><td colspan=7></td></tr>';
?>
</table>
<div class="nav">
<?php
$ly=$year; $lm=$month-1; if($lm==0){$lm=12;$ly--;}
$ny=$year; $nm=$month+1; if($nm==13){$nm=1;$ny++;}
if($ly>=1901) echo '<a href="'.$file.'?year='.$ly.'&month='.$lm.'">« 上月</a> ';
echo ' ';
if($ny<=2020) echo '<a href="'.$file.'?year='.$ny.'&month='.$nm.'">下月 »</a>';
?>
</div>
<form method="get" class="nav">
年份:
<select name="year" onchange="this.form.submit()">
<?php for($y=2020;$y>=1901;$y--): ?>
<option value="<?=$y?>" <?=($y==$year?'selected':'')?>><?=$y?></option>
<?php endfor; ?>
</select>
月份:
<select name="month" onchange="this.form.submit()">
<?php for($m=1;$m<=12;$m++): ?>
<option value="<?=$m?>" <?=($m==$month?'selected':'')?>><?=$m?></option>
<?php endfor; ?>
</select>
</form>
</body></html>
JieQi 算法(基於太陽黃經),在對應日期標註「立春」「清明」等[月-日]=>名稱 映射表(元旦、情人節、勞動節、國慶等)$everymonth[$j][0] 非 0 即為閏月位置(1-12),對應農曆月份陣列下標$mten/$mtwelve 中的下標將上述代碼保存為 calendar.php 放入 Web 目錄,瀏覽器訪問即可。無需安裝擴展、配置資料庫、佈置前端構建,適合快速嵌入 CMS、個人博客、企業內網門戶等場景。