24節氣演算法

風的王子發表於2016-08-03
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;


/**
 *describe:24節氣
 * 
 * 注:程式中使用到的計算節氣公式、節氣世紀常量等相關資訊參照
 * 
 * 程式的執行得出的節氣結果絕大多數是正確的,有少數部份是有誤差的
 */
public class SolarTerms {
    private static final double D = 0.2422;
    private final static Map<String, Integer[]> INCREASE_OFFSETMAP = new HashMap<String, Integer[]>();// +1偏移
    private final static Map<String, Integer[]> DECREASE_OFFSETMAP = new HashMap<String, Integer[]>();// -1偏移


    /** 24節氣 **/
    private static enum SolarTermsEnum {
        LICHUN, // --立春
        YUSHUI, // --雨水
        JINGZHE, // --驚蟄
        CHUNFEN, // 春分
        QINGMING, // 清明
        GUYU, // 穀雨
        LIXIA, // 立夏
        XIAOMAN, // 小滿
        MANGZHONG, // 芒種
        XIAZHI, // 夏至
        XIAOSHU, // 小暑
        DASHU, // 大暑
        LIQIU, // 立秋
        CHUSHU, // 處暑
        BAILU, // 白露
        QIUFEN, // 秋分
        HANLU, // 寒露
        SHUANGJIANG, // 霜降
        LIDONG, // 立冬
        XIAOXUE, // 小雪
        DAXUE, // 大雪
        DONGZHI, // 冬至
        XIAOHAN, // 小寒
        DAHAN;// 大寒
    }


    static {
        DECREASE_OFFSETMAP.put(SolarTermsEnum.YUSHUI.name(), new Integer[] { 2026 });// 雨水
        INCREASE_OFFSETMAP.put(SolarTermsEnum.CHUNFEN.name(), new Integer[] { 2084 });// 春分
        INCREASE_OFFSETMAP.put(SolarTermsEnum.XIAOMAN.name(), new Integer[] { 2008 });// 小滿
        INCREASE_OFFSETMAP.put(SolarTermsEnum.MANGZHONG.name(), new Integer[] { 1902 });// 芒種
        INCREASE_OFFSETMAP.put(SolarTermsEnum.XIAZHI.name(), new Integer[] { 1928 });// 夏至
        INCREASE_OFFSETMAP.put(SolarTermsEnum.XIAOSHU.name(), new Integer[] { 1925, 2016 });// 小暑
        INCREASE_OFFSETMAP.put(SolarTermsEnum.DASHU.name(), new Integer[] { 1922 });// 大暑
        INCREASE_OFFSETMAP.put(SolarTermsEnum.LIQIU.name(), new Integer[] { 2002 });// 立秋
        INCREASE_OFFSETMAP.put(SolarTermsEnum.BAILU.name(), new Integer[] { 1927 });// 白露
        INCREASE_OFFSETMAP.put(SolarTermsEnum.QIUFEN.name(), new Integer[] { 1942 });// 秋分
        INCREASE_OFFSETMAP.put(SolarTermsEnum.SHUANGJIANG.name(), new Integer[] { 2089 });// 霜降
        INCREASE_OFFSETMAP.put(SolarTermsEnum.LIDONG.name(), new Integer[] { 2089 });// 立冬
        INCREASE_OFFSETMAP.put(SolarTermsEnum.XIAOXUE.name(), new Integer[] { 1978 });// 小雪
        INCREASE_OFFSETMAP.put(SolarTermsEnum.DAXUE.name(), new Integer[] { 1954 });// 大雪
        DECREASE_OFFSETMAP.put(SolarTermsEnum.DONGZHI.name(), new Integer[] { 1918, 2021 });// 冬至


        INCREASE_OFFSETMAP.put(SolarTermsEnum.XIAOHAN.name(), new Integer[] { 1982 });// 小寒
        DECREASE_OFFSETMAP.put(SolarTermsEnum.XIAOHAN.name(), new Integer[] { 2019 });// 小寒


        INCREASE_OFFSETMAP.put(SolarTermsEnum.DAHAN.name(), new Integer[] { 2082 });// 大寒
    }


    // 定義一個二維陣列,第一維陣列儲存的是20世紀的節氣C值,第二維陣列儲存的是21世紀的節氣C值,023個,依次代表立春、雨水...大寒節氣的C值
    private static final double[][] CENTURY_ARRAY = {
            { 4.6295, 19.4599, 6.3826, 21.4155, 5.59, 20.888, 6.318, 21.86, 6.5, 22.2, 7.928, 23.65, 8.35, 23.95, 8.44,
                    23.822, 9.098, 24.218, 8.218, 23.08, 7.9, 22.6, 6.11, 20.84 },
            { 3.87, 18.73, 5.63, 20.646, 4.81, 20.1, 5.52, 21.04, 5.678, 21.37, 7.108, 22.83, 7.5, 23.13, 7.646,
                    23.042, 8.318, 23.438, 7.438, 22.36, 7.18, 21.94, 5.4055, 20.12 } };


    /**
     * 
     * @param year
     *            年份
     * @param name
     *            節氣的名稱
     * @return 返回節氣是相應月份的第幾天
     */
    public static int getSolarTermNum(int year, String name) {


        double centuryValue = 0;// 節氣的世紀值,每個節氣的每個世紀值都不同
        name = name.trim().toUpperCase();
        int ordinal = SolarTermsEnum.valueOf(name).ordinal();


        int centuryIndex = -1;
        if (year >= 1901 && year <= 2000) {// 20世紀
            centuryIndex = 0;
        } else if (year >= 2001 && year <= 2100) {// 21世紀
            centuryIndex = 1;
        } else {
            throw new RuntimeException("不支援此年份:" + year + ",目前只支援1901年到2100年的時間範圍");
        }
        centuryValue = CENTURY_ARRAY[centuryIndex][ordinal];
        int dateNum = 0;
        /**
         * 計算 num =[Y*D+C]-L這是傳說中的壽星通用公式 公式解讀:年數的後2位乘0.2422加C(即:centuryValue)取整數後,減閏年數
         */
        int y = year % 100;// 步驟1:取年分的後兩位數
        if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {// 閏年
            if (ordinal == SolarTermsEnum.XIAOHAN.ordinal() || ordinal == SolarTermsEnum.DAHAN.ordinal()
                    || ordinal == SolarTermsEnum.LICHUN.ordinal() || ordinal == SolarTermsEnum.YUSHUI.ordinal()) {
                // 注意:凡閏年31日前閏年數要減一,即:L=[(Y-1)/4],因為小寒、大寒、立春、雨水這兩個節氣都小於31日,所以 y = y-1
                y = y - 1;// 步驟2
            }
        }
        dateNum = (int) (y * D + centuryValue) - (int) (y / 4);// 步驟3,使用公式[Y*D+C]-L計算
        dateNum += specialYearOffset(year, name);// 步驟4,加上特殊的年分的節氣偏移量
        return dateNum;
    }


    /**
     * 特例,特殊的年分的節氣偏移量,由於公式並不完善,所以算出的個別節氣的第幾天數並不準確,在此返回其偏移量
     * 
     * @param year
     *            年份
     * @param name
     *            節氣名稱
     * @return 返回其偏移量
     */
    public static int specialYearOffset(int year, String name) {
        int offset = 0;
        offset += getOffset(DECREASE_OFFSETMAP, year, name, -1);
        offset += getOffset(INCREASE_OFFSETMAP, year, name, 1);


        return offset;
    }


    public static int getOffset(Map<String, Integer[]> map, int year, String name, int offset) {
        int off = 0;
        Integer[] years = map.get(name);
        if (null != years) {
            for (int i : years) {
                if (i == year) {
                    off = offset;
                    break;
                }
            }
        }
        return off;
    }


    public static String solarTermToString(int year) {
        StringBuffer sb = new StringBuffer();
        // sb.append("---").append(year);
        // if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {// 閏年
        // sb.append(" 閏年");
        // } else {
        // sb.append(" 平年");
        // }


        // sb.append("\n").append("立春:2月").append(getSolarTermNum(year, SolarTermsEnum.LICHUN.name())).append("日,雨水:2月")
        // .append(getSolarTermNum(year, SolarTermsEnum.YUSHUI.name())).append("日,驚蟄:3月").append(
        // getSolarTermNum(year, SolarTermsEnum.JINGZHE.name())).append("日,春分:3月").append(
        // getSolarTermNum(year, SolarTermsEnum.CHUNFEN.name())).append("日,清明:4月").append(
        // getSolarTermNum(year, SolarTermsEnum.QINGMING.name())).append("日,穀雨:4月").append(
        // getSolarTermNum(year, SolarTermsEnum.GUYU.name())).append("日,立夏:5月").append(
        // getSolarTermNum(year, SolarTermsEnum.LIXIA.name())).append("日,小滿:5月").append(
        // getSolarTermNum(year, SolarTermsEnum.XIAOMAN.name())).append("日,芒種:6月").append(
        // getSolarTermNum(year, SolarTermsEnum.MANGZHONG.name())).append("日,夏至:6月").append(
        // getSolarTermNum(year, SolarTermsEnum.XIAZHI.name())).append("日,小暑:7月").append(
        // getSolarTermNum(year, SolarTermsEnum.XIAOSHU.name())).append("日,大暑:7月").append(
        // getSolarTermNum(year, SolarTermsEnum.DASHU.name())).append("日,\n立秋:8月").append(
        // getSolarTermNum(year, SolarTermsEnum.LIQIU.name())).append("日,處暑:8月").append(
        // getSolarTermNum(year, SolarTermsEnum.CHUSHU.name())).append("日,白露:9月").append(
        // getSolarTermNum(year, SolarTermsEnum.BAILU.name())).append("日,秋分:9月").append(
        // getSolarTermNum(year, SolarTermsEnum.QIUFEN.name())).append("日,寒露:10月").append(
        // getSolarTermNum(year, SolarTermsEnum.HANLU.name())).append("日,霜降:10月").append(
        // getSolarTermNum(year, SolarTermsEnum.SHUANGJIANG.name())).append("日,立冬:11月").append(
        // getSolarTermNum(year, SolarTermsEnum.LIDONG.name())).append("日,小雪:11月").append(
        // getSolarTermNum(year, SolarTermsEnum.XIAOXUE.name())).append("日,大雪:12月").append(
        // getSolarTermNum(year, SolarTermsEnum.DAXUE.name())).append("日,冬至:12月").append(
        // getSolarTermNum(year, SolarTermsEnum.DONGZHI.name())).append("日,小寒:1月").append(
        // getSolarTermNum(year, SolarTermsEnum.XIAOHAN.name())).append("日,大寒:1月").append(
        // getSolarTermNum(year, SolarTermsEnum.DAHAN.name()));
        sb.append("2:").append(getSolarTermNum(year, SolarTermsEnum.LICHUN.name())).append(",2:").append(
                getSolarTermNum(year, SolarTermsEnum.YUSHUI.name())).append(",3:").append(
                getSolarTermNum(year, SolarTermsEnum.JINGZHE.name())).append(",3:").append(
                getSolarTermNum(year, SolarTermsEnum.CHUNFEN.name())).append(",4:").append(
                getSolarTermNum(year, SolarTermsEnum.QINGMING.name())).append(",4:").append(
                getSolarTermNum(year, SolarTermsEnum.GUYU.name())).append(",5:").append(
                getSolarTermNum(year, SolarTermsEnum.LIXIA.name())).append(",5:").append(
                getSolarTermNum(year, SolarTermsEnum.XIAOMAN.name())).append(",6:").append(
                getSolarTermNum(year, SolarTermsEnum.MANGZHONG.name())).append(",6:").append(
                getSolarTermNum(year, SolarTermsEnum.XIAZHI.name())).append(",7:").append(
                getSolarTermNum(year, SolarTermsEnum.XIAOSHU.name())).append(",7:").append(
                getSolarTermNum(year, SolarTermsEnum.DASHU.name())).append(",8:").append(
                getSolarTermNum(year, SolarTermsEnum.LIQIU.name())).append(",8:").append(
                getSolarTermNum(year, SolarTermsEnum.CHUSHU.name())).append(",9:").append(
                getSolarTermNum(year, SolarTermsEnum.BAILU.name())).append(",9:").append(
                getSolarTermNum(year, SolarTermsEnum.QIUFEN.name())).append(",10:").append(
                getSolarTermNum(year, SolarTermsEnum.HANLU.name())).append(",10:").append(
                getSolarTermNum(year, SolarTermsEnum.SHUANGJIANG.name())).append(",11:").append(
                getSolarTermNum(year, SolarTermsEnum.LIDONG.name())).append(",11:").append(
                getSolarTermNum(year, SolarTermsEnum.XIAOXUE.name())).append(",12:").append(
                getSolarTermNum(year, SolarTermsEnum.DAXUE.name())).append(",12:").append(
                getSolarTermNum(year, SolarTermsEnum.DONGZHI.name())).append(",1:").append(
                getSolarTermNum(year, SolarTermsEnum.XIAOHAN.name())).append(",1:").append(
                getSolarTermNum(year, SolarTermsEnum.DAHAN.name()));


        return sb.toString();
    }


    public static int[][] genSolarTermsDays() {
        Calendar cal = Calendar.getInstance();
        int year = cal.get(Calendar.YEAR);
        return linear2TwoDimensional(solarTermToString(year).split(","));
    }


    /**
     * 一位陣列轉換為二維陣列
     * 
     * @param arr
     * @return
     */
    public static int[][] linear2TwoDimensional(String[] arr) {
        int s[][] = new int[arr.length][];// 宣告一個二維陣列
        for (int i = 0; i < arr.length; i++) {
            String[] sdate = arr[i].split(":");
            int[] idate = new int[sdate.length];
            for (int j = 0; j < sdate.length; j++) {
                idate[j] = Integer.valueOf(sdate[j].trim());
            }
            s[i] = idate;


        }
        return s;
    }


}

相關文章