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

中曆演算法深度解析:公曆/農曆轉換與節氣計算完整實作

     編輯:Java 相關知識

前言

中國傳統曆法結合太陽年(公曆)與月相週期(農曆),演算法相對複雜。本文詳細解析公曆星期計算、農曆日期轉換、二十四節氣判定等核心演算法,並提供可直接使用的 Java 完整實作碼(適用西元 1901-2100 年)。

一、公曆星期計算演算法

基準:西元 1 年 1 月 1 日為星期一。利用 400 年週期特性簡化計算。

/**
 * 計算公曆日期對應星期幾
 * @return 1=週日, 2=週一, ..., 7=週六
 */
public static int dayOfWeek(int year, int month, int day) {
    int w = 1; // 1/1/1 是週一,起始值設為週日
    
    // 利用 400 年週期減少運算量
    year = (year - 1) % 400 + 1;
    
    int leapYears = (year - 1) / 4;      // 每 4 年一閏
    leapYears -= (year - 1) / 100;       // 但整百年不閏
    leapYears += (year - 1) / 400;       // 除非整 400 年
    
    int commonYears = year - 1 - leapYears;
    
    w += commonYears;        // 平年推移 1 天
    w += 2 * leapYears;      // 閏年推移 2 天
    w += dayOfYear(year, month, day); // 加上今年經過天數
    
    return (w - 1) % 7 + 1;  // 映射到 1-7
}

二、農曆核心演算法架構

農曆採用「定氣朔」法:以冬至為基準定義月份大小、閏月安排。演算法核心包含三大資料表:

  1. 月份大小壓縮表:每年 2 位元組(16 bits),高 4 bits 記錄閏月月份,低 12 bits 記錄 12 個月大小(1=大月30天, 0=小月29天)
  2. 節氣查表:sectionalTermMap (節氣) 與 principleTermMap (中氣),每月 28-29 年週期資料
  3. 大閏月年份表:記錄閏月為 30 天的特殊年份索引

三、關鍵計算方法詳解

1. 基準日對齊法


// 兩個錨點提升計算效率
private static final int BASE_YEAR = 1901;
private static final int BASE_CHINESE_YEAR = 4597; // 1901/1/1 = 農曆 4598 年 11 月 11 日

// 2000 年為第二錨點
private static final int BASE_YEAR_2000 = 2000;
private static final int BASE_CHINESE_YEAR_2000 = 4696; // 2000/1/1 = 農曆 4697 年 11 月 25 日

2. 公曆轉農曆核心流程


public int computeChineseFields() {
    if (gregorianYear < 1901 || gregorianYear > 2100) return -1;
    
    // 選擇較近錨點減少迴圈次數
    int startYear = (gregorianYear >= 2000) ? 2000 : 1901;
    int chineseYear = (gregorianYear >= 2000) ? 4696 : 4597;
    int chineseMonth = 11;
    int chineseDate = (gregorianYear >= 2000) ? 25 : 11;
    
    // 計算兩日期相差天數
    int daysDiff = calculateDayDifference(startYear, gregorianYear, gregorianMonth, gregorianDate);
    
    chineseDate += daysDiff;
    
    // 向前推算農曆年月
    while (chineseDate > daysInChineseMonth(chineseYear, chineseMonth)) {
        chineseDate -= daysInChineseMonth(chineseYear, chineseMonth);
        int nextMonth = nextChineseMonth(chineseYear, chineseMonth);
        if (Math.abs(nextMonth) < Math.abs(chineseMonth)) chineseYear++;
        chineseMonth = nextMonth;
    }
    
    this.chineseYear = chineseYear;
    this.chineseMonth = chineseMonth;
    this.chineseDate = chineseDate;
    return 0;
}

3. 節氣計算:查表法 + 四年一閏修正


public static int sectionalTerm(int year, int month) {
    int idx = 0;
    int relYear = year - 1900; // 1901 -> 1
    
    // 找到對應年份區間
    while (relYear >= sectionalTermYear[month-1][idx]) idx++;
    
    // 基礎日期 + 4 年週期修正
    int term = sectionalTermMap[month-1][4*idx + relYear%4];
    
    // 硬編碼特例修正(天文觀測微調)
    if (relYear == 121 && month == 4) term = 5;  // 2021 清明
    if (relYear == 132 && month == 4) term = 5;  // 2032 清明
    if (relYear == 194 && month == 6) term = 6;  // 2094 芒種
    
    return term;
}

四、完整類別結構 ChineseCalendar

核心屬性:

  • 公曆欄位:year, month, date, isLeap, dayOfYear, dayOfWeek
  • 農曆欄位:chineseYear, chineseMonth (負數表閏月), chineseDate
  • 節氣欄位:sectionalTerm (節氣日), principleTerm (中氣日)
  • 常數陣列:天干、地支、生肖、月名、節氣名

輸出格式化範例


public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append(String.format("公曆: %d-%02d-%02d ", gy, gm, gd));
    sb.append(String.format("農曆: %d年%s%d月%d日 ", 
        chineseYear + 1, 
        chineseMonth < 0 ? "閏" : "",
        Math.abs(chineseMonth), chineseDate));
    sb.append(String.format("干支: %s%s年 ", 
        STEMS[chineseYear % 10], BRANCHES[chineseYear % 12]));
    if (sectionalTerm == gm) sb.append("節氣: ").append(SECTIONAL_TERMS[gm-1]);
    if (principleTerm == gm) sb.append("節氣: ").append(PRINCIPLE_TERMS[gm-1]);
    return sb.toString();
}

五、二百年萬年曆輸出格式

程式可產出 1901-2100 年完整對照表,格式範例(2000 年 1 月):

一月
日 一 二 三 四 五 六
    1/25 2/26 3/27 4/28 5/29 立春 臘月
8/ 2 9/ 3 10/ 4 11/ 5 12/ 6 13/ 7 14/ 8
15/ 9 16/10 17/11 18/12 19/13 20/14 雨水 22/16
23/17 24/18 25/19 26/20 27/21 28/22 29/23
30/24 31/25

說明:

  • 1/25 = 公曆 1 日 / 農曆 25 日
  • 立春、雨水 = 節氣名稱
  • 臘月 = 農曆 12 月首日

六、使用方式與擴展建議


// 命令列使用
java ChineseCalendarGB day 2024 10 15  // 查詢單日
java ChineseCalendarGB year 2024       // 輸出全年曆
java ChineseCalendarGB month 2024 10   // 輸出單月曆

現代化改良方向

  1. 整合 java.time 套件(LocalDate, Chronology)實作標準化介面
  2. 擴充支援範圍至更早/更晚年份(需擴充壓縮表)
  3. 加入時區支援與精確交節時刻計算
  4. 提供 Spring Boot Starter 或 Maven 中央倉庫發布

參考資源

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