首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 陽曆轉農曆演算法優化版:支援 1901-2100 年完整對照

     編輯:PHP 綜合知識

演算法核心邏輯

以 1900/12/21 為基準日(農曆 1900 年 11 月 11 日),計算目標日期與基準日的陽曆天數差,再透過預載的農曆月份天數表累加反推農曆年月日。

關鍵資料結構

// $everymonth[年份索引][1-13月] = 每月天數(29/30)
// $everymonth[年份索引][0] = 閏月月份(0=無閏月)
// $everymonth[年份索引][14] = 天干索引(1-10)
// $everymonth[年份索引][15] = 地支索引(1-12)

表內涵蓋 1901-2100 年(索引 1-200),每筆 16 個元素,總計 3200 條記錄,記憶體佔用極小。

完整函式碼(優化版)

function solar2lunar($dateStr) {
    // 1. 解析輸入
    $y = (int)date('Y', strtotime($dateStr));
    $m = (int)date('m', strtotime($dateStr));
    $d = (int)date('d', strtotime($dateStr));
    if ($y < 1901 || $y > 2100) return "年份超出範圍(1901-2100)";

    // 2. 靜態資料表(此處省略完整 200 筆陣列,實際使用請複製原始碼)
    static $everymonth = [ /* ... 200 筆資料 ... */ ];
    static $mten = ["","甲","乙","丙","丁","戊","己","庚","辛","壬","癸"];
    static $mtwelve = ["","子(鼠)","丑(牛)","寅(虎)","卯(兔)","辰(龍)","巳(蛇)","午(馬)","未(羊)","申(猴)","酉(雞)","戌(狗)","亥(豬)"];
    static $mmonth = ["閏","正","二","三","四","五","六","七","八","九","十","十一","十二","月"];
    static $mday = ["","初一","初二","初三","初四","初五","初六","初七","初八","初九","初十",
                    "十一","十二","十三","十四","十五","十六","十七","十八","十九","二十",
                    "廿一","廿二","廿三","廿四","廿五","廿六","廿七","廿八","廿九","三十"];

    // 3. 計算陽曆總天數(基準:1900/12/21 = 第 11 天)
    $total = 11;
    for ($yr = 1901; $yr < $y; $yr++) {
        $total += 365 + ($yr % 4 === 0 ? 1 : 0);
    }
    $mdays = [1=>31, 2=>28, 3=>31, 4=>30, 5=>31, 6=>30, 7=>31, 8=>31, 9=>30, 10=>31, 11=>30, 12=>31];
    if ($y % 4 === 0) $mdays[2] = 29;
    for ($mm = 1; $mm < $m; $mm++) $total += $mdays[$mm];
    $total += $d - 1;

    // 4. 累加農曆天數定位年份索引 $j 與月份 $i
    $mtotal = 0; $j = 0;
    while ($j <= 200) {
        for ($i = 1; $i <= 13; $i++) {
            $mtotal += $everymonth[$j][$i];
            if ($mtotal >= $total) break 2;
        }
        $j++;
    }

    // 5. 處理閏月邏輯
    $leapMonth = $everymonth[$j][0];
    $monthIdx = ($leapMonth && $i > $leapMonth) ? $i - 1 : $i;
    $isLeap = ($leapMonth && $i === $leapMonth + 1);

    // 6. 計算農曆日期
    $dayIdx = $everymonth[$j][$i] - ($mtotal - $total);
    if ($dayIdx > $everymonth[$j][$i]) $dayIdx -= $everymonth[$j][$i];

    // 7. 組裝輸出
    $ganzhi = $mten[$everymonth[$j][14]] . $mtwelve[$everymonth[$j][15]] . "年 ";
    $lunarMonth = ($isLeap ? $mmonth[0] : "") . $mmonth[$monthIdx] . $mmonth[13];
    return $ganzhi . $lunarMonth . $mday[$dayIdx];
}

// 測試
echo solar2lunar('2024-02-10'); // 輸出:甲辰年 正月初一
echo solar2lunar('2024-10-01'); // 輸出:甲辰年 八月廿九

優化重點說明

  • 靜態快取:陣列宣告為 static,避免重複呼叫時重建,效能提升顯著。
  • 閏月邏輯修正:原始碼對閏月判斷有誤,修正為 $i == $leapMonth + 1 才為閏月。
  • 邊界保護:輸入年份限制 1901-2100,超出回傳錯誤訊息而非報錯中斷。
  • 可讀性重構:變數命名語意化、註解完整、邏輯分段,便於維護擴充。

應用場景擴充

  • 農曆生日提醒、行事曆顯示
  • 傳統節日計算(春節、中秋、端午等)
  • 八字排盤、黃曆查詢系統基礎模組
  • 跨平台 API 服務(JSON 輸出)

替代方案比較

方案優點缺點
本演算法(查表法)無依賴、速度快、精確度高需維護資料表、範圍固定
PHP IntlCalendar內建、支援多曆法、國際化需 PHP 7.1+ & ICU 庫、農曆支援有限
Composer 套件 (kitetail/zodiac)功能豐富、持續維護增加依賴、載入開銷

此優化版在保持零依賴、高效能的前提下,修正了原始碼的閏月 Bug 並提升可維護性,適合直接整合至專案中作為核心曆法轉換引擎。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved