!169 干支纪年转换拓展

Merge pull request !169 from dcbai/v5-dev
This commit is contained in:
Looly 2020-08-28 16:00:17 +08:00 committed by Gitee
commit be6d287b6b
2 changed files with 261 additions and 29 deletions

View File

@ -4,9 +4,12 @@ import cn.hutool.core.convert.NumberChineseFormatter;
import cn.hutool.core.util.StrUtil;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import static cn.hutool.core.util.NumberUtil.parseInt;
/**
* 农历日期工具最大支持到2055年
@ -15,19 +18,23 @@ import java.util.List;
* @since 5.1.1
*/
public class ChineseDate {
// private static final Date baseDate = DateUtil.parseDate("1900-01-31");
/**
* 1900-01-31
*/
private static final long baseDate = -2206425943000L;
//农历年
private final int year;
//农历月
private final int month;
//农历日
private final int day;
//公历年
private final int gyear;
//公历月
private final int gmonth;
//公里日
private final int gday;
//是否闰年
private boolean leap;
private final String[] chineseNumber = {"", "", "", "", "", "", "", "", "", "", "十一", "十二"};
@ -61,6 +68,78 @@ public class ChineseDate {
0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160,//2080-2089
0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252,//2090-2099
};
/**
* 1900-2100各年的24节气日期速查表
* 此表来自https://github.com/jjonline/calendar.js/blob/master/calendar.js
*/
private final String[] sTermInfo = new String[]{
"9778397bd097c36b0b6fc9274c91aa", "97b6b97bd19801ec9210c965cc920e", "97bcf97c3598082c95f8c965cc920f",
"97bd0b06bdb0722c965ce1cfcc920f", "b027097bd097c36b0b6fc9274c91aa", "97b6b97bd19801ec9210c965cc920e",
"97bcf97c359801ec95f8c965cc920f", "97bd0b06bdb0722c965ce1cfcc920f", "b027097bd097c36b0b6fc9274c91aa",
"97b6b97bd19801ec9210c965cc920e", "97bcf97c359801ec95f8c965cc920f", "97bd0b06bdb0722c965ce1cfcc920f",
"b027097bd097c36b0b6fc9274c91aa", "9778397bd19801ec9210c965cc920e", "97b6b97bd19801ec95f8c965cc920f",
"97bd09801d98082c95f8e1cfcc920f", "97bd097bd097c36b0b6fc9210c8dc2", "9778397bd197c36c9210c9274c91aa",
"97b6b97bd19801ec95f8c965cc920e", "97bd09801d98082c95f8e1cfcc920f", "97bd097bd097c36b0b6fc9210c8dc2",
"9778397bd097c36c9210c9274c91aa", "97b6b97bd19801ec95f8c965cc920e", "97bcf97c3598082c95f8e1cfcc920f",
"97bd097bd097c36b0b6fc9210c8dc2", "9778397bd097c36c9210c9274c91aa", "97b6b97bd19801ec9210c965cc920e",
"97bcf97c3598082c95f8c965cc920f", "97bd097bd097c35b0b6fc920fb0722", "9778397bd097c36b0b6fc9274c91aa",
"97b6b97bd19801ec9210c965cc920e", "97bcf97c3598082c95f8c965cc920f", "97bd097bd097c35b0b6fc920fb0722",
"9778397bd097c36b0b6fc9274c91aa", "97b6b97bd19801ec9210c965cc920e", "97bcf97c359801ec95f8c965cc920f",
"97bd097bd097c35b0b6fc920fb0722", "9778397bd097c36b0b6fc9274c91aa", "97b6b97bd19801ec9210c965cc920e",
"97bcf97c359801ec95f8c965cc920f", "97bd097bd097c35b0b6fc920fb0722", "9778397bd097c36b0b6fc9274c91aa",
"97b6b97bd19801ec9210c965cc920e", "97bcf97c359801ec95f8c965cc920f", "97bd097bd07f595b0b6fc920fb0722",
"9778397bd097c36b0b6fc9210c8dc2", "9778397bd19801ec9210c9274c920e", "97b6b97bd19801ec95f8c965cc920f",
"97bd07f5307f595b0b0bc920fb0722", "7f0e397bd097c36b0b6fc9210c8dc2", "9778397bd097c36c9210c9274c920e",
"97b6b97bd19801ec95f8c965cc920f", "97bd07f5307f595b0b0bc920fb0722", "7f0e397bd097c36b0b6fc9210c8dc2",
"9778397bd097c36c9210c9274c91aa", "97b6b97bd19801ec9210c965cc920e", "97bd07f1487f595b0b0bc920fb0722",
"7f0e397bd097c36b0b6fc9210c8dc2", "9778397bd097c36b0b6fc9274c91aa", "97b6b97bd19801ec9210c965cc920e",
"97bcf7f1487f595b0b0bb0b6fb0722", "7f0e397bd097c35b0b6fc920fb0722", "9778397bd097c36b0b6fc9274c91aa",
"97b6b97bd19801ec9210c965cc920e", "97bcf7f1487f595b0b0bb0b6fb0722", "7f0e397bd097c35b0b6fc920fb0722",
"9778397bd097c36b0b6fc9274c91aa", "97b6b97bd19801ec9210c965cc920e", "97bcf7f1487f531b0b0bb0b6fb0722",
"7f0e397bd097c35b0b6fc920fb0722", "9778397bd097c36b0b6fc9274c91aa", "97b6b97bd19801ec9210c965cc920e",
"97bcf7f1487f531b0b0bb0b6fb0722", "7f0e397bd07f595b0b6fc920fb0722", "9778397bd097c36b0b6fc9274c91aa",
"97b6b97bd19801ec9210c9274c920e", "97bcf7f0e47f531b0b0bb0b6fb0722", "7f0e397bd07f595b0b0bc920fb0722",
"9778397bd097c36b0b6fc9210c91aa", "97b6b97bd197c36c9210c9274c920e", "97bcf7f0e47f531b0b0bb0b6fb0722",
"7f0e397bd07f595b0b0bc920fb0722", "9778397bd097c36b0b6fc9210c8dc2", "9778397bd097c36c9210c9274c920e",
"97b6b7f0e47f531b0723b0b6fb0722", "7f0e37f5307f595b0b0bc920fb0722", "7f0e397bd097c36b0b6fc9210c8dc2",
"9778397bd097c36b0b70c9274c91aa", "97b6b7f0e47f531b0723b0b6fb0721", "7f0e37f1487f595b0b0bb0b6fb0722",
"7f0e397bd097c35b0b6fc9210c8dc2", "9778397bd097c36b0b6fc9274c91aa", "97b6b7f0e47f531b0723b0b6fb0721",
"7f0e27f1487f595b0b0bb0b6fb0722", "7f0e397bd097c35b0b6fc920fb0722", "9778397bd097c36b0b6fc9274c91aa",
"97b6b7f0e47f531b0723b0b6fb0721", "7f0e27f1487f531b0b0bb0b6fb0722", "7f0e397bd097c35b0b6fc920fb0722",
"9778397bd097c36b0b6fc9274c91aa", "97b6b7f0e47f531b0723b0b6fb0721", "7f0e27f1487f531b0b0bb0b6fb0722",
"7f0e397bd097c35b0b6fc920fb0722", "9778397bd097c36b0b6fc9274c91aa", "97b6b7f0e47f531b0723b0b6fb0721",
"7f0e27f1487f531b0b0bb0b6fb0722", "7f0e397bd07f595b0b0bc920fb0722", "9778397bd097c36b0b6fc9274c91aa",
"97b6b7f0e47f531b0723b0787b0721", "7f0e27f0e47f531b0b0bb0b6fb0722", "7f0e397bd07f595b0b0bc920fb0722",
"9778397bd097c36b0b6fc9210c91aa", "97b6b7f0e47f149b0723b0787b0721", "7f0e27f0e47f531b0723b0b6fb0722",
"7f0e397bd07f595b0b0bc920fb0722", "9778397bd097c36b0b6fc9210c8dc2", "977837f0e37f149b0723b0787b0721",
"7f07e7f0e47f531b0723b0b6fb0722", "7f0e37f5307f595b0b0bc920fb0722", "7f0e397bd097c35b0b6fc9210c8dc2",
"977837f0e37f14998082b0787b0721", "7f07e7f0e47f531b0723b0b6fb0721", "7f0e37f1487f595b0b0bb0b6fb0722",
"7f0e397bd097c35b0b6fc9210c8dc2", "977837f0e37f14998082b0787b06bd", "7f07e7f0e47f531b0723b0b6fb0721",
"7f0e27f1487f531b0b0bb0b6fb0722", "7f0e397bd097c35b0b6fc920fb0722", "977837f0e37f14998082b0787b06bd",
"7f07e7f0e47f531b0723b0b6fb0721", "7f0e27f1487f531b0b0bb0b6fb0722", "7f0e397bd097c35b0b6fc920fb0722",
"977837f0e37f14998082b0787b06bd", "7f07e7f0e47f531b0723b0b6fb0721", "7f0e27f1487f531b0b0bb0b6fb0722",
"7f0e397bd07f595b0b0bc920fb0722", "977837f0e37f14998082b0787b06bd", "7f07e7f0e47f531b0723b0b6fb0721",
"7f0e27f1487f531b0b0bb0b6fb0722", "7f0e397bd07f595b0b0bc920fb0722", "977837f0e37f14998082b0787b06bd",
"7f07e7f0e47f149b0723b0787b0721", "7f0e27f0e47f531b0b0bb0b6fb0722", "7f0e397bd07f595b0b0bc920fb0722",
"977837f0e37f14998082b0723b06bd", "7f07e7f0e37f149b0723b0787b0721", "7f0e27f0e47f531b0723b0b6fb0722",
"7f0e397bd07f595b0b0bc920fb0722", "977837f0e37f14898082b0723b02d5", "7ec967f0e37f14998082b0787b0721",
"7f07e7f0e47f531b0723b0b6fb0722", "7f0e37f1487f595b0b0bb0b6fb0722", "7f0e37f0e37f14898082b0723b02d5",
"7ec967f0e37f14998082b0787b0721", "7f07e7f0e47f531b0723b0b6fb0722", "7f0e37f1487f531b0b0bb0b6fb0722",
"7f0e37f0e37f14898082b0723b02d5", "7ec967f0e37f14998082b0787b06bd", "7f07e7f0e47f531b0723b0b6fb0721",
"7f0e37f1487f531b0b0bb0b6fb0722", "7f0e37f0e37f14898082b072297c35", "7ec967f0e37f14998082b0787b06bd",
"7f07e7f0e47f531b0723b0b6fb0721", "7f0e27f1487f531b0b0bb0b6fb0722", "7f0e37f0e37f14898082b072297c35",
"7ec967f0e37f14998082b0787b06bd", "7f07e7f0e47f531b0723b0b6fb0721", "7f0e27f1487f531b0b0bb0b6fb0722",
"7f0e37f0e366aa89801eb072297c35", "7ec967f0e37f14998082b0787b06bd", "7f07e7f0e47f149b0723b0787b0721",
"7f0e27f1487f531b0b0bb0b6fb0722", "7f0e37f0e366aa89801eb072297c35", "7ec967f0e37f14998082b0723b06bd",
"7f07e7f0e47f149b0723b0787b0721", "7f0e27f0e47f531b0723b0b6fb0722", "7f0e37f0e366aa89801eb072297c35",
"7ec967f0e37f14998082b0723b06bd", "7f07e7f0e37f14998083b0787b0721", "7f0e27f0e47f531b0723b0b6fb0722",
"7f0e37f0e366aa89801eb072297c35", "7ec967f0e37f14898082b0723b02d5", "7f07e7f0e37f14998082b0787b0721",
"7f07e7f0e47f531b0723b0b6fb0722", "7f0e36665b66aa89801e9808297c35", "665f67f0e37f14898082b0723b02d5",
"7ec967f0e37f14998082b0787b0721", "7f07e7f0e47f531b0723b0b6fb0722", "7f0e36665b66a449801e9808297c35",
"665f67f0e37f14898082b0723b02d5", "7ec967f0e37f14998082b0787b06bd", "7f07e7f0e47f531b0723b0b6fb0721",
"7f0e36665b66a449801e9808297c35", "665f67f0e37f14898082b072297c35", "7ec967f0e37f14998082b0787b06bd",
"7f07e7f0e47f531b0723b0b6fb0721", "7f0e26665b66a449801e9808297c35", "665f67f0e37f1489801eb072297c35",
"7ec967f0e37f14998082b0787b06bd", "7f07e7f0e47f531b0723b0b6fb0721", "7f0e27f1487f531b0b0bb0b6fb0722"};
//农历节日 *表示放假日
private final String[] lFtv = new String[]{
@ -70,7 +149,8 @@ public class ChineseDate {
"0408 佛诞节 ", "0505 端午节", "0606 天贶节 姑姑节", "0624 彝族火把节",
"0707 七夕情人节", "0714 鬼节(南方)", "0715 盂兰节", "0730 地藏节",
"0815 中秋节", "0909 重阳节", "1001 祭祖节", "1117 阿弥陀佛圣诞",
"1208 腊八节 释迦如来成道日", "1223 过小年", "1229 腊月二十九", "1230 除夕"};
"1208 腊八节 释迦如来成道日", "1223 过小年", "1229 腊月二十九", "1230 除夕"
};
/**
* 构造方法传入日期
@ -80,7 +160,6 @@ public class ChineseDate {
public ChineseDate(Date date) {
// 求出和1900年1月31日相差的天数
int offset = (int) ((date.getTime() - baseDate) / DateUnit.DAY.getMillis());
// 计算农历年份
// 用offset减去每农历年的天数计算当天是农历第几天offset是当年的第几天
int daysOfYear;
@ -93,6 +172,9 @@ public class ChineseDate {
}
offset -= daysOfYear;
}
Calendar instance = Calendar.getInstance();
instance.setTime(date);
gyear = instance.get(Calendar.YEAR);
year = iYear;
// 计算农历月份
@ -126,14 +208,16 @@ public class ChineseDate {
--iMonth;
}
}
// offset小于0时也要校正
if (offset < 0) {
offset += daysOfMonth;
--iMonth;
}
month = iMonth;
gmonth = instance.get(Calendar.MARCH) + 1;
day = offset + 1;
gday = instance.get(Calendar.DATE);
}
/**
@ -149,6 +233,24 @@ public class ChineseDate {
this.month = chineseMonth;
this.year = chineseYear;
this.leap = DateUtil.isLeapYear(chineseYear);
//先判断传入的月份是不是闰月
int leapMonth = this.leapMonth(chineseYear);
Integer[] integers = lunar2solar(chineseYear, chineseMonth, chineseDay, chineseMonth == leapMonth);
if (integers != null) {
//初始化公历年
this.gday = integers[2];
//初始化公历月
this.gmonth = integers[1];
//初始化公历日
this.gyear = integers[0];
} else {
//初始化公历年
this.gday = -1;
//初始化公历月
this.gmonth = -1;
//初始化公历日
this.gyear = -1;
}
}
/**
@ -170,6 +272,7 @@ public class ChineseDate {
return this.month;
}
/**
* 获得农历月份中文例如二月十二月或者润一月
*
@ -269,6 +372,17 @@ public class ChineseDate {
return (cyclicalm(num));
}
/**
* 干支纪年信息
*
* @return 获得天干地支的年月日信息
*/
public String getCyclicalYMD() {
if (gyear >= 1900 && gmonth > 0 && gday > 0)
return (cyclicalm(gyear, gmonth, gday));
return "";
}
/**
* 转换为标准的日期格式来表示农历日期例如2020-01-13
*
@ -295,6 +409,58 @@ public class ChineseDate {
return (Gan[num % 10] + Zhi[num % 12]);
}
/**
* 这里同步处理年月日的天干地支信息
*
* @param Y
* @param M
* @param D
* @return
*/
private String cyclicalm(int Y, int M, int D) {
String gzyear = cyclicalm(year - 1900 + 36);
//天干地支处理
//返回当月为几日开始
int firstNode = this.getTerm(Y, (M * 2 - 1));
// 依据12节气修正干支月
String gzM = this.cyclicalm((Y - 1900) * 12 + M + 11);
if (D >= firstNode) {
gzM = this.cyclicalm((Y - 1900) * 12 + M + 12);
}
int dayCyclical = (int) ((DateUtil.parseDate(Y + "-" + M + "-" + "1").getTime() - baseDate + 2592000000L) / DateUnit.DAY.getMillis()) + 10;
String gzD = this.cyclicalm(dayCyclical + D - 1);
return gzyear + "" + gzM + "" + gzD + "";
}
/**
* 根据节气修正干支月
*
* @param y
* @param n
* @return
*/
private int getTerm(int y, int n) {
if (y < 1900 || y > 2100) {
return -1;
}
if (n < 1 || n > 24) {
return -1;
}
String _table = this.sTermInfo[y - 1900];
Integer[] _info = new Integer[6];
for (int i = 0; i < 6; i++) {
_info[i] = parseInt("0x" + _table.substring(i * 5, 5 * (i + 1)));
}
String[] _calday = new String[24];
for (int i = 0; i < 6; i++) {
_calday[4*i] = _info[i].toString().substring(0, 1);
_calday[4*i+1] = _info[i].toString().substring(1, 3);
_calday[4*i+2] = _info[i].toString().substring(3, 4);
_calday[4*i+3] = _info[i].toString().substring(4, 6);
}
return parseInt(_calday[n - 1]);
}
/**
* 传回农历 y年的总天数
*
@ -324,7 +490,6 @@ public class ChineseDate {
return 0;
}
/**
* 传回农历 y年m月的总天数
*
@ -346,5 +511,58 @@ public class ChineseDate {
return (int) (lunarInfo[y - 1900] & 0xf);
}
// ------------------------------------------------------- private method end
/**
* 通过农历年月日信息 返回公历信息 提供给构造函数
*
* @param chineseYear 农历年
* @param chineseMonth 农历月
* @param chineseDay 农历日
* @param isLeapMonth 传入的月是不是闰月
* @return
*/
private Integer[] lunar2solar(int chineseYear, int chineseMonth, int chineseDay, boolean isLeapMonth) {
//超出了最大极限值
if (chineseYear == 2100 && chineseMonth == 12 && chineseDay > 1 || chineseYear == 1900 && chineseMonth == 1 && chineseDay < 31) {
return null;
}
int day = this.monthDays(chineseYear, chineseMonth);
int _day = day;
if (isLeapMonth) {
_day = this.leapDays(chineseYear);
}
//参数合法性效验
if (chineseYear < 1900 || chineseYear > 2100 || chineseDay > _day) {
return null;
}
//计算农历的时间差
int offset = 0;
for (int i = 1900; i < chineseYear; i++) {
offset += this.yearDays(i);
}
int leap = 0;
boolean isAdd = false;
for (int i = 1; i < chineseMonth; i++) {
leap = this.leapMonth(chineseYear);
if (!isAdd) {//处理闰月
if (leap <= i && leap > 0) {
offset += this.leapDays(chineseYear);
isAdd = true;
}
}
offset += this.monthDays(chineseYear, i);
}
//转换闰月农历 需补充该年闰月的前一个月的时差
if (isLeapMonth) {
offset += day;
}
//1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点) -2203804800000
DateTime date = DateUtil.date(((offset + chineseDay - 31) * 86400000L) - 2203804800000L);
int year = date.year();
int month = date.month() + 1;
int d = date.dayOfMonth();
return new Integer[]{year, month, d};
}
// ------------------------------------------------------- private method end
}

View File

@ -45,4 +45,18 @@ public class ChineseDateTest {
date = new ChineseDate(DateUtil.parseDate("1996-07-15"));
Assert.assertEquals("丙子鼠年 五月三十", date.toString());
}
@Test
public void getCyclicalYMDTest(){
//通过公历构建
ChineseDate chineseDate = new ChineseDate(DateUtil.parseDate("1993-01-06"));
String cyclicalYMD = chineseDate.getCyclicalYMD();
Assert.assertEquals("壬申年癸丑月丁亥日",cyclicalYMD);
}
@Test
public void getCyclicalYMDTest_2(){
//通过农历构建
ChineseDate chineseDate = new ChineseDate(1992,12,14);
String cyclicalYMD = chineseDate.getCyclicalYMD();
Assert.assertEquals("壬申年癸丑月丁亥日",cyclicalYMD);
}
}