LunarCalender.java 13.2 KB
package com.wd.common.utils;


import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * 中国农历工具类
 *
 * @author qts
 * @data 2023/7/27.
 */
public class LunarCalender {
    private int year; // 农历的年份
    private int month;
    private int day;
    private String lunarMonth; // 农历的月份
    private boolean leap;
    public int leapMonth = 0; // 闰的是哪个月

    final static String chineseNumber[] = {"正", "二", "三", "四", "五", "六", "七",
            "八", "九", "十", "冬", "腊"};
    static SimpleDateFormat chineseDateFormat = new SimpleDateFormat(
            "yyyy年MM月dd日");
    final static long[] lunarInfo = new long[]{
            0x4bd8, 0x4ae0, 0xa570, 0x54d5, 0xd260, 0xd950, 0x5554, 0x56af, 0x9ad0, 0x55d2,
            0x4ae0, 0xa5b6, 0xa4d0, 0xd250, 0xd255, 0xb54f, 0xd6a0, 0xada2, 0x95b0, 0x4977,
            0x497f, 0xa4b0, 0xb4b5, 0x6a50, 0x6d40, 0xab54, 0x2b6f, 0x9570, 0x52f2, 0x4970,
            0x6566, 0xd4a0, 0xea50, 0x6a95, 0x5adf, 0x2b60, 0x86e3, 0x92ef, 0xc8d7, 0xc95f,
            0xd4a0, 0xd8a6, 0xb55f, 0x56a0, 0xa5b4, 0x25df, 0x92d0, 0xd2b2, 0xa950, 0xb557,
            0x6ca0, 0xb550, 0x5355, 0x4daf, 0xa5b0, 0x4573, 0x52bf, 0xa9a8, 0xe950, 0x6aa0,
            0xaea6, 0xab50, 0x4b60, 0xaae4, 0xa570, 0x5260, 0xf263, 0xd950, 0x5b57, 0x56a0,
            0x96d0, 0x4dd5, 0x4ad0, 0xa4d0, 0xd4d4, 0xd250, 0xd558, 0xb540, 0xb6a0, 0x95a6,
            0x95bf, 0x49b0, 0xa974, 0xa4b0, 0xb27a, 0x6a50, 0x6d40, 0xaf46, 0xab60, 0x9570,
            0x4af5, 0x4970, 0x64b0, 0x74a3, 0xea50, 0x6b58, 0x5ac0, 0xab60, 0x96d5, 0x92e0,
            0xc960, 0xd954, 0xd4a0, 0xda50, 0x7552, 0x56a0, 0xabb7, 0x25d0, 0x92d0, 0xcab5,
            0xa950, 0xb4a0, 0xbaa4, 0xad50, 0x55d9, 0x4ba0, 0xa5b0, 0x5176, 0x52bf, 0xa930,
            0x7954, 0x6aa0, 0xad50, 0x5b52, 0x4b60, 0xa6e6, 0xa4e0, 0xd260, 0xea65, 0xd530,
            0x5aa0, 0x76a3, 0x96d0, 0x4afb, 0x4ad0, 0xa4d0, 0xd0b6, 0xd25f, 0xd520, 0xdd45,
            0xb5a0, 0x56d0, 0x55b2, 0x49b0, 0xa577, 0xa4b0, 0xaa50, 0xb255, 0x6d2f, 0xada0,
            0x4b63, 0x937f, 0x49f8, 0x4970, 0x64b0, 0x68a6, 0xea5f, 0x6b20, 0xa6c4, 0xaaef,
            0x92e0, 0xd2e3, 0xc960, 0xd557, 0xd4a0, 0xda50, 0x5d55, 0x56a0, 0xa6d0, 0x55d4,
            0x52d0, 0xa9b8, 0xa950, 0xb4a0, 0xb6a6, 0xad50, 0x55a0, 0xaba4, 0xa5b0, 0x52b0,
            0xb273, 0x6930, 0x7337, 0x6aa0, 0xad50, 0x4b55, 0x4b6f, 0xa570, 0x54e4, 0xd260,
            0xe968, 0xd520, 0xdaa0, 0x6aa6, 0x56df, 0x4ae0, 0xa9d4, 0xa4d0, 0xd150, 0xf252,
            0xd520};


    final static String[] Gan = new String[]{"甲", "乙", "丙", "丁", "戊", "己", "庚",
            "辛", "壬", "癸"};
    final static String[] Zhi = new String[]{"子", "丑", "寅", "卯", "辰", "巳", "午",
            "未", "申", "酉", "戌", "亥"};

    // ====== 传回农历 y年的总天数 1900--2100
    public int yearDays(int y) {
        int i, sum = 348;
        for (i = 0x8000; i > 0x8; i >>= 1) {
            if ((lunarInfo[y - 1900] & i) != 0) {
                sum += 1;
            }
        }
        return (sum + leapDays(y));
    }

    // ====== 传回农历 y年闰月的天数
    public int leapDays(int y) {
        if (leapMonth(y) != 0) {
            if ((lunarInfo[y - 1899] & 0xf) != 0) {
                return 30;
            } else {
                return 29;
            }
        } else {
            return 0;
        }
    }

    // ====== 传回农历 y年闰哪个月 1-12 , 没闰传回 0
    public int leapMonth(int y) {
        long var = lunarInfo[y - 1900] & 0xf;
        return (int) (var == 0xf ? 0 : var);
    }

    // ====== 传回农历 y年m月的总天数
    public int monthDays(int y, int m) {
        if ((lunarInfo[y - 1900] & (0x10000 >> m)) == 0) {
            return 29;
        } else {
            return 30;
        }
    }

    // ====== 传回农历 y年的生肖
    public String animalsYear(int year) {
        final String[] Animals = new String[]{"鼠", "牛", "虎", "兔", "龙", "蛇",
                "马", "羊", "猴", "鸡", "狗", "猪"};
        return Animals[(year - 4) % 12] + "年";
    }

    // ====== 传入 月日的offset 传回干支, 0=甲子
    final private static String cyclicalm(int num) {
        return (Gan[num % 10] + Zhi[num % 12]);
    }

    // ====== 传入 offset 传回干支, 0=甲子
    final public String cyclical(int year, int month, int day) {
        int num = year - 1900 + 36;
        //立春日期
        int term2 = sTerm(year, 2);
        if (month > 2 || (month == 2 && day >= term2)) {
            num = num + 0;
        } else {
            num = num - 1;
        }
        return (cyclicalm(num));
    }

    public static String getChinaDayString(int day) {//将农历day日格式化成农历表示的字符串
        String chineseTen[] = {"初", "十", "廿", "三"};
        String chineseDay[] = {"一", "二", "三", "四", "五", "六", "七",
                "八", "九", "十"};
        String var = "";
        if (day != 20 && day != 30) {
            var = chineseTen[(int) ((day - 1) / 10)] + chineseDay[(int) ((day - 1) % 10)];
        } else if (day != 20) {
            var = chineseTen[(int) (day / 10)] + "十";
        } else {
            var = "二十";
        }
        return var;
    }

    /*
     * 计算公历nY年nM月nD日和bY年bM月bD日渐相差多少天
     * */
    public int getDaysOfTwoDate(int bY, int bM, int bD, int nY, int nM, int nD) {
        Date baseDate = null;
        Date nowaday = null;
        try {
            baseDate = chineseDateFormat.parse(bY + "年" + bM + "月" + bD + "日");
            nowaday = chineseDateFormat.parse(nY + "年" + nM + "月" + nD + "日");
        } catch (ParseException e) {
            e.printStackTrace();
        }
        int offset = 0;
        if(nowaday != null && baseDate !=null) {
            // 求出相差的天数
            offset = (int) ((nowaday.getTime() - baseDate.getTime()) / 86400000L);
        }
        return offset;
    }

    /*农历lunYear年lunMonth月lunDay日
     * isLeap 当前年月是否是闰月
     * 从农历日期转换成公历日期
     * */
    public Calendar getSunDate(int lunYear, int lunMonth, int lunDay, boolean isLeap) {
        //公历1900年1月31日为1900年正月初一
        int years = lunYear - 1900;
        int days = 0;
        for (int i = 0; i < years; i++) {
            days += yearDays(1900 + i);//农历某年总天数
        }
        for (int i = 1; i < lunMonth; i++) {
            days += monthDays(lunYear, i);
        }
        if (leapMonth(lunYear) != 0 && lunMonth > leapMonth(lunYear)) {
            days += leapDays(lunYear);//lunYear年闰月天数
        }
        if (isLeap) {
            days += monthDays(lunYear, lunMonth);//lunYear年lunMonth月 闰月
        }
        days += lunDay;
        days = days - 1;
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, 1900);
        cal.set(Calendar.MONTH, 0);
        cal.set(Calendar.DAY_OF_MONTH, 31);
        cal.add(Calendar.DATE, days);
	    /*
	    Date date=cal.getTime();
	    int year_c = cal.get(Calendar.YEAR);
	    int month_c = cal.get(Calendar.MONTH)+1;
	    int day_c = cal.get(Calendar.DAY_OF_MONTH);
	    */
        return cal;
    }

    public String getLunarString(int year, int month, int day) {

        int var = getLunarDateINT(year, month, day);
        int lYear = (int) var / 10000;
        int lMonth = (int) (var % 10000) / 100;
        int lDay = var - lYear * 10000 - lMonth * 100;
        String lunY = cyclical(year, month, day) + "年";

        String lunM = "";
        int testMonth = getSunDate(lYear, lMonth, lDay, false).get(Calendar.MONTH) + 1;
        if (testMonth != month) {
            lunM = "闰";
        }
        lunM += chineseNumber[(lMonth - 1) % 12] + "月";
        //int leap = leapMonth(lYear);
        String lunD = getChinaDayString(lDay);
        int animalsYear = 0;
        int term2 = sTerm(year, 2);//立春
        if (month > 2 || (month == 2 && day >= term2)) {
            animalsYear = year;
        } else {
            animalsYear = year - 1;
        }
        return lunY + lunM + lunD;
    }

    /*
     * 将公历year年month月day日转换成农历
     * 返回格式为20140506(int)
     * */
    public int getLunarDateINT(int year, int month, int day) {
        int iYear, LYear, LMonth, LDay, daysOfYear = 0;
        // 求出和1900年1月31日相差的天数
        //year =1908;
        //month = 3;
        //day =3;
        int offset = getDaysOfTwoDate(1900, 1, 31, year, month, day);
        //Log.i("--ss--","公历:"+year+"-"+month+"-"+day+":"+offset);
        // 用offset减去每农历年的天数
        // 计算当天是农历第几天
        // i最终结果是农历的年份
        // offset是当年的第几天
        for (iYear = 1900; iYear < 2100 && offset > 0; iYear++) {
            daysOfYear = yearDays(iYear);
            offset -= daysOfYear;
            //Log.i("--ss--","农历:"+iYear+":"+daysOfYear+"/"+offset);
        }

        if (offset < 0) {
            offset += daysOfYear;
            iYear--;
            //Log.i("--ss--","农历:"+iYear+":"+daysOfYear+"/"+offset);
        }
        // 农历年份
        LYear = iYear;

        leapMonth = leapMonth(iYear); // 闰哪个月,1-12
        leap = false;

        // 用当年的天数offset,逐个减去每月(农历)的天数,求出当天是本月的第几天
        int iMonth = 1, daysOfMonth = 0;
        for (iMonth = 1; iMonth < 13 && offset > 0; iMonth++) {
            // 闰月
            if (leapMonth > 0 && iMonth == (leapMonth + 1) && !leap) {
                --iMonth;
                leap = true;
                daysOfMonth = leapDays(iYear);
            } else {
                daysOfMonth = monthDays(iYear, iMonth);
            }
            // 解除闰月
            if (leap && iMonth == (leapMonth + 1)) {
                leap = false;
            }

            offset -= daysOfMonth;
            //Log.i("--ss--","农历:"+iYear+"-"+iMonth+":"+daysOfMonth+"/"+offset);
        }
        // offset为0时,并且刚才计算的月份是闰月,要校正
        if (offset == 0 && leapMonth > 0 && iMonth == leapMonth + 1) {
            if (leap) {
                leap = false;
            } else {
                leap = true;
                --iMonth;
            }
        }
        // offset小于0时,也要校正
        if (offset < 0) {
            offset += daysOfMonth;
            --iMonth;
            //Log.i("--ss--","农历:"+iYear+"-"+iMonth+":"+daysOfMonth+"/"+offset);
        }
        LMonth = iMonth;
        LDay = offset + 1;
        //Log.i("--ss--","农历:"+LYear+"-"+LMonth+"-"+LDay);
        return LYear * 10000 + LMonth * 100 + LDay;
    }
    public String toString() {
        if (chineseNumber[(month - 1) % 12] == "正" && getChinaDayString(day) == "初一") {
            return "农历" + year + "年";
        } else if (getChinaDayString(day) == "初一") {
            return chineseNumber[(month - 1) % 12] + "月";
        } else {
            return getChinaDayString(day);
        }
        // return year + "年" + (leap ? "闰" : "") + chineseNumber[month - 1] +
        // "月" + getChinaDayString(day);
    }

    /*
     * public static void main(String[] args) { System.out.println(new
     * LunarCalendar().getLunarDate(2012, 1, 23)); }
     */

    public int getLeapMonth() {
        return leapMonth;
    }

    public void setLeapMonth(int leapMonth) {
        this.leapMonth = leapMonth;
    }

    /**
     * 得到当前日期对应的阴历月份
     *
     * @return
     */
    public String getLunarMonth() {
        return lunarMonth;
    }

    public void setLunarMonth(String lunarMonth) {
        this.lunarMonth = lunarMonth;
    }

    /**
     * 得到当前年对应的农历年份
     *
     * @return
     */
    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }


    private int sTerm(int y, int n) {
        int[] sTermInfo = new int[]{0, 21208, 42467, 63836, 85337, 107014,
                128867, 150921, 173149, 195551, 218072, 240693,
                263343, 285989, 308563, 331033, 353350, 375494,
                397447, 419210, 440795, 462224, 483532, 504758};
        Calendar cal = Calendar.getInstance();
        cal.set(1900, 0, 6, 2, 5, 0);
        long temp = cal.getTime().getTime();
        cal.setTime(new Date((long) ((31556925974.7 * (y - 1900) + sTermInfo[n] * 60000L) + temp)));
        int a = cal.get(Calendar.DAY_OF_MONTH);
        return a;
    }

    public static String convertToLunarTianGan(int year) {
        String[] tianGan = {"甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"};
        int index = (year - 4) % 10;
        return tianGan[index];
    }
    public static String convertToLunarDiZhi(int year) {
        String[] diZhi = {"子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"};
        int index = (year - 4) % 12;
        return diZhi[index];
    }

    public static String convertToChineseMonth(int month) {
        String[] chineseMonths = {"正月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "冬月", "腊月"};
        return chineseMonths[month - 1];
    }



}