1
0
mirror of synced 2025-12-24 22:38:00 +08:00
Files
lunar-java/src/main/java/com/nlf/calendar/Solar.java
2024-11-12 21:14:19 +08:00

921 lines
21 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.nlf.calendar;
import java.util.*;
import com.nlf.calendar.util.HolidayUtil;
import com.nlf.calendar.util.LunarUtil;
import com.nlf.calendar.util.SolarUtil;
/**
* 阳历日期
*
* @author 6tail
*/
public class Solar {
/**
* 时区
*/
public static final TimeZone TIME_ZONE = TimeZone.getTimeZone("GMT+8");
/**
* 2000年儒略日数(2000-1-1 12:00:00 UTC)
*/
public static final double J2000 = 2451545;
/**
* 年
*/
private final int year;
/**
* 月
*/
private final int month;
/**
* 日
*/
private final int day;
/**
* 时
*/
private final int hour;
/**
* 分
*/
private final int minute;
/**
* 秒
*/
private final int second;
/**
* 默认使用当前日期初始化
*/
public Solar() {
this(new Date());
}
/**
* 通过年月日初始化
*
* @param year 年
* @param month 月1到12
* @param day 日1到31
*/
public Solar(int year, int month, int day) {
this(year, month, day, 0, 0, 0);
}
/**
* 通过年月日初始化
*
* @param year 年
* @param month 月1到12
* @param day 日1到31
* @param hour 小时0到23
* @param minute 分钟0到59
* @param second 秒钟0到59
*/
public Solar(int year, int month, int day, int hour, int minute, int second) {
if (1582 == year && 10 == month) {
if (day > 4 && day < 15) {
throw new IllegalArgumentException(String.format("wrong solar year %d month %d day %d", year, month, day));
}
}
if (month < 1 || month > 12) {
throw new IllegalArgumentException(String.format("wrong month %d", month));
}
if (day < 1) {
throw new IllegalArgumentException(String.format("wrong day %d", day));
}
int days = SolarUtil.getDaysOfMonth(year, month);
if (day > days) {
throw new IllegalArgumentException(String.format("only %d days in solar year %d month %d", days, year, month));
}
if (hour < 0 || hour > 23) {
throw new IllegalArgumentException(String.format("wrong hour %d", hour));
}
if (minute < 0 || minute > 59) {
throw new IllegalArgumentException(String.format("wrong minute %d", minute));
}
if (second < 0 || second > 59) {
throw new IllegalArgumentException(String.format("wrong second %d", second));
}
this.year = year;
this.month = month;
this.day = day;
this.hour = hour;
this.minute = minute;
this.second = second;
}
/**
* 通过日期初始化
*
* @param date 日期
*/
public Solar(Date date) {
Calendar c = Calendar.getInstance(TIME_ZONE);
c.setTime(date);
c.set(Calendar.MILLISECOND, 0);
year = c.get(Calendar.YEAR);
month = c.get(Calendar.MONTH) + 1;
day = c.get(Calendar.DATE);
hour = c.get(Calendar.HOUR_OF_DAY);
minute = c.get(Calendar.MINUTE);
second = c.get(Calendar.SECOND);
}
/**
* 通过日历初始化
*
* @param calendar 日历
*/
public Solar(Calendar calendar) {
calendar.set(Calendar.MILLISECOND, 0);
year = calendar.get(Calendar.YEAR);
month = calendar.get(Calendar.MONTH) + 1;
day = calendar.get(Calendar.DATE);
hour = calendar.get(Calendar.HOUR_OF_DAY);
minute = calendar.get(Calendar.MINUTE);
second = calendar.get(Calendar.SECOND);
}
/**
* 通过儒略日初始化
*
* @param julianDay 儒略日
*/
public Solar(double julianDay) {
int d = (int) (julianDay + 0.5);
double f = julianDay + 0.5 - d;
int c;
if (d >= 2299161) {
c = (int) ((d - 1867216.25) / 36524.25);
d += 1 + c - (int) (c * 1D / 4);
}
d += 1524;
int year = (int) ((d - 122.1) / 365.25);
d -= (int) (365.25 * year);
int month = (int) (d * 1D / 30.601);
d -= (int) (30.601 * month);
int day = d;
if (month > 13) {
month -= 13;
year -= 4715;
} else {
month -= 1;
year -= 4716;
}
f *= 24;
int hour = (int) f;
f -= hour;
f *= 60;
int minute = (int) f;
f -= minute;
f *= 60;
int second = (int) Math.round(f);
if (second > 59) {
second -= 60;
minute++;
}
if (minute > 59) {
minute -= 60;
hour++;
}
if (hour > 23) {
hour -= 24;
day += 1;
}
this.year = year;
this.month = month;
this.day = day;
this.hour = hour;
this.minute = minute;
this.second = second;
}
/**
* 通过指定日期获取阳历
*
* @param date 日期
* @return 阳历
*/
public static Solar fromDate(Date date) {
return new Solar(date);
}
/**
* 通过指定日历获取阳历
*
* @param calendar 日历
* @return 阳历
*/
public static Solar fromCalendar(Calendar calendar) {
return new Solar(calendar);
}
/**
* 通过指定儒略日获取阳历
*
* @param julianDay 儒略日
* @return 阳历
*/
public static Solar fromJulianDay(double julianDay) {
return new Solar(julianDay);
}
/**
* 通过指定年月日获取阳历
*
* @param year 年
* @param month 月1到12
* @param day 日1到31
* @return 阳历
*/
public static Solar fromYmd(int year, int month, int day) {
return new Solar(year, month, day);
}
/**
* 通过指定年月日时分获取阳历
*
* @param year 年
* @param month 月1到12
* @param day 日1到31
* @param hour 小时0到23
* @param minute 分钟0到59
* @param second 秒钟0到59
* @return 阳历
*/
public static Solar fromYmdHms(int year, int month, int day, int hour, int minute, int second) {
return new Solar(year, month, day, hour, minute, second);
}
/**
* 通过八字获取阳历列表晚子时日柱按当天起始年为1900
*
* @param yearGanZhi 年柱
* @param monthGanZhi 月柱
* @param dayGanZhi 日柱
* @param timeGanZhi 时柱
* @return 符合的阳历列表
*/
public static List<Solar> fromBaZi(String yearGanZhi, String monthGanZhi, String dayGanZhi, String timeGanZhi) {
return fromBaZi(yearGanZhi, monthGanZhi, dayGanZhi, timeGanZhi, 2);
}
/**
* 通过八字获取阳历列表起始年为1900
*
* @param yearGanZhi 年柱
* @param monthGanZhi 月柱
* @param dayGanZhi 日柱
* @param timeGanZhi 时柱
* @param sect 流派2晚子时日柱按当天1晚子时日柱按明天
* @return 符合的阳历列表
*/
public static List<Solar> fromBaZi(String yearGanZhi, String monthGanZhi, String dayGanZhi, String timeGanZhi, int sect) {
return fromBaZi(yearGanZhi, monthGanZhi, dayGanZhi, timeGanZhi, sect, 1900);
}
/**
* 通过八字获取阳历列表
*
* @param yearGanZhi 年柱
* @param monthGanZhi 月柱
* @param dayGanZhi 日柱
* @param timeGanZhi 时柱
* @param sect 流派2晚子时日柱按当天1晚子时日柱按明天
* @param baseYear 起始年
* @return 符合的阳历列表
*/
public static List<Solar> fromBaZi(String yearGanZhi, String monthGanZhi, String dayGanZhi, String timeGanZhi, int sect, int baseYear) {
sect = (1 == sect) ? 1 : 2;
List<Solar> l = new ArrayList<Solar>();
// 月地支距寅月的偏移值
int m = LunarUtil.find(monthGanZhi.substring(1), LunarUtil.ZHI, -1) - 2;
if (m < 0) {
m += 12;
}
// 月天干要一致
if (((LunarUtil.find(yearGanZhi.substring(0, 1), LunarUtil.GAN, -1) + 1) * 2 + m) % 10 != LunarUtil.find(monthGanZhi.substring(0, 1), LunarUtil.GAN, -1)) {
return l;
}
// 1年的立春是辛酉序号57
int y = LunarUtil.getJiaZiIndex(yearGanZhi) - 57;
if (y < 0) {
y += 60;
}
y++;
// 节令偏移值
m *= 2;
// 时辰地支转时刻,子时按零点算
int h = LunarUtil.find(timeGanZhi.substring(1), LunarUtil.ZHI, -1) * 2;
int[] hours = {h};
if (0 == h && 2 == sect) {
hours = new int[]{0, 23};
}
int startYear = baseYear - 1;
// 结束年
Calendar c = Calendar.getInstance(TIME_ZONE);
c.setTime(new Date());
c.set(Calendar.MILLISECOND, 0);
int endYear = c.get(Calendar.YEAR);
while (y <= endYear) {
if (y >= startYear) {
// 立春为寅月的开始
List<Solar> jieQiList = new ArrayList<Solar>(Lunar.fromYmd(y, 1, 1).getJieQiTable().values());
// 节令推移,年干支和月干支就都匹配上了
Solar solarTime = jieQiList.get(4 + m);
if (solarTime.getYear() >= baseYear) {
// 日干支和节令干支的偏移值
int d = LunarUtil.getJiaZiIndex(dayGanZhi) - LunarUtil.getJiaZiIndex(solarTime.getLunar().getDayInGanZhiExact2());
if (d < 0) {
d += 60;
}
if (d > 0) {
// 从节令推移天数
solarTime = solarTime.next(d);
}
for (int hour : hours) {
int mi = 0;
int s = 0;
if (d == 0 && hour == solarTime.getHour()) {
// 如果正好是节令当天,且小时和节令的小时数相等的极端情况,把分钟和秒钟带上
mi = solarTime.getMinute();
s = solarTime.getSecond();
}
// 验证一下
Solar solar = Solar.fromYmdHms(solarTime.getYear(), solarTime.getMonth(), solarTime.getDay(), hour, mi, s);
Lunar lunar = solar.getLunar();
String dgz = (2 == sect) ? lunar.getDayInGanZhiExact2() : lunar.getDayInGanZhiExact();
if (lunar.getYearInGanZhiExact().equals(yearGanZhi) && lunar.getMonthInGanZhiExact().equals(monthGanZhi) && dgz.equals(dayGanZhi) && lunar.getTimeInGanZhi().equals(timeGanZhi)) {
l.add(solar);
}
}
}
}
y += 60;
}
return l;
}
/**
* 是否闰年
*
* @return true/false 闰年/非闰年
*/
public boolean isLeapYear() {
return SolarUtil.isLeapYear(year);
}
/**
* 获取星期0代表周日1代表周一
*
* @return 0123456
*/
public int getWeek() {
return ((int) (getJulianDay() + 0.5) + 7000001) % 7;
}
/**
* 获取星期的中文
*
* @return 日一二三四五六
*/
public String getWeekInChinese() {
return SolarUtil.WEEK[getWeek()];
}
/**
* 获取节日,有可能一天会有多个节日
*
* @return 劳动节等
*/
public List<String> getFestivals() {
List<String> l = new ArrayList<String>();
//获取几月几日对应的节日
String f = SolarUtil.FESTIVAL.get(month + "-" + day);
if (null != f) {
l.add(f);
}
//计算几月第几个星期几对应的节日
int weeks = (int) Math.ceil(day / 7D);
//星期几0代表星期天
int week = getWeek();
f = SolarUtil.WEEK_FESTIVAL.get(month + "-" + weeks + "-" + week);
if (null != f) {
l.add(f);
}
if (day + 7 > SolarUtil.getDaysOfMonth(year, month)) {
f = SolarUtil.WEEK_FESTIVAL.get(month + "-0-" + week);
if (null != f) {
l.add(f);
}
}
return l;
}
/**
* 获取非正式的节日,有可能一天会有多个节日
*
* @return 非正式的节日列表,如中元节
*/
public List<String> getOtherFestivals() {
List<String> l = new ArrayList<String>();
List<String> fs = SolarUtil.OTHER_FESTIVAL.get(month + "-" + day);
if (null != fs) {
l.addAll(fs);
}
return l;
}
/**
* 获取星座
*
* @return 星座
* @deprecated 使用getXingZuo
*/
public String getXingzuo() {
return getXingZuo();
}
/**
* 获取星座
*
* @return 星座
*/
public String getXingZuo() {
int index = 11;
int y = month * 100 + day;
if (y >= 321 && y <= 419) {
index = 0;
} else if (y >= 420 && y <= 520) {
index = 1;
} else if (y >= 521 && y <= 621) {
index = 2;
} else if (y >= 622 && y <= 722) {
index = 3;
} else if (y >= 723 && y <= 822) {
index = 4;
} else if (y >= 823 && y <= 922) {
index = 5;
} else if (y >= 923 && y <= 1023) {
index = 6;
} else if (y >= 1024 && y <= 1122) {
index = 7;
} else if (y >= 1123 && y <= 1221) {
index = 8;
} else if (y >= 1222 || y <= 119) {
index = 9;
} else if (y <= 218) {
index = 10;
}
return SolarUtil.XINGZUO[index];
}
/**
* 获取年份
*
* @return 如2015
*/
public int getYear() {
return year;
}
/**
* 获取月份
*
* @return 1到12
*/
public int getMonth() {
return month;
}
/**
* 获取日期
*
* @return 1到31之间的数字
*/
public int getDay() {
return day;
}
/**
* 获取小时
*
* @return 0到23之间的数字
*/
public int getHour() {
return hour;
}
/**
* 获取分钟
*
* @return 0到59之间的数字
*/
public int getMinute() {
return minute;
}
/**
* 获取秒钟
*
* @return 0到59之间的数字
*/
public int getSecond() {
return second;
}
/**
* 获取农历
*
* @return 农历
*/
public Lunar getLunar() {
return new Lunar(this);
}
/**
* 获取儒略日
*
* @return 儒略日
*/
public double getJulianDay() {
int y = this.year;
int m = this.month;
double d = this.day + ((this.second * 1D / 60 + this.minute) / 60 + this.hour) / 24;
int n = 0;
boolean g = y * 372 + m * 31 + (int) d >= 588829;
if (m <= 2) {
m += 12;
y--;
}
if (g) {
n = (int) (y * 1D / 100);
n = 2 - n + (int) (n * 1D / 4);
}
return (int) (365.25 * (y + 4716)) + (int) (30.6001 * (m + 1)) + d + n - 1524.5;
}
@Override
public String toString() {
return toYmd();
}
public String toYmd() {
return String.format("%04d-%02d-%02d", year, month, day);
}
public String toYmdHms() {
return toYmd() + " " + String.format("%02d:%02d:%02d", hour, minute, second);
}
public String toFullString() {
StringBuilder s = new StringBuilder();
s.append(toYmdHms());
if (isLeapYear()) {
s.append(" ");
s.append("闰年");
}
s.append(" ");
s.append("星期");
s.append(getWeekInChinese());
for (String f : getFestivals()) {
s.append(" (");
s.append(f);
s.append(")");
}
for (String f : getOtherFestivals()) {
s.append(" (");
s.append(f);
s.append(")");
}
s.append(" ");
s.append(getXingZuo());
s.append("");
return s.toString();
}
/**
* 阳历日期相减,获得相差天数
*
* @param solar 阳历
* @return 天数
*/
public int subtract(Solar solar) {
return SolarUtil.getDaysBetween(solar.getYear(), solar.getMonth(), solar.getDay(), year, month, day);
}
/**
* 阳历日期相减,获得相差分钟数
*
* @param solar 阳历
* @return 分钟数
*/
public int subtractMinute(Solar solar) {
int days = subtract(solar);
int cm = hour * 60 + minute;
int sm = solar.getHour() * 60 + solar.getMinute();
int m = cm - sm;
if (m < 0) {
m += 1440;
days--;
}
m += days * 1440;
return m;
}
/**
* 是否在指定日期之后
*
* @param solar 阳历
* @return true/false
*/
public boolean isAfter(Solar solar) {
if (year > solar.getYear()) {
return true;
}
if (year < solar.getYear()) {
return false;
}
if (month > solar.getMonth()) {
return true;
}
if (month < solar.getMonth()) {
return false;
}
if (day > solar.getDay()) {
return true;
}
if (day < solar.getDay()) {
return false;
}
if (hour > solar.getHour()) {
return true;
}
if (hour < solar.getHour()) {
return false;
}
if (minute > solar.getMinute()) {
return true;
}
if (minute < solar.getMinute()) {
return false;
}
return second > solar.getSecond();
}
/**
* 是否在指定日期之前
*
* @param solar 阳历
* @return true/false
*/
public boolean isBefore(Solar solar) {
if (year > solar.getYear()) {
return false;
}
if (year < solar.getYear()) {
return true;
}
if (month > solar.getMonth()) {
return false;
}
if (month < solar.getMonth()) {
return true;
}
if (day > solar.getDay()) {
return false;
}
if (day < solar.getDay()) {
return true;
}
if (hour > solar.getHour()) {
return false;
}
if (hour < solar.getHour()) {
return true;
}
if (minute > solar.getMinute()) {
return false;
}
if (minute < solar.getMinute()) {
return true;
}
return second < solar.getSecond();
}
/**
* 年推移
*
* @param years 年数
* @return 阳历
*/
public Solar nextYear(int years) {
int y = year + years;
int m = month;
int d = day;
if (1582 == y && 10 == m) {
if (d > 4 && d < 15) {
d += 10;
}
} else if (2 == m) {
if (d > 28) {
if (!SolarUtil.isLeapYear(y)) {
d = 28;
}
}
}
return fromYmdHms(y, m, d, hour, minute, second);
}
/**
* 月推移
*
* @param months 月数
* @return 阳历
*/
public Solar nextMonth(int months) {
SolarMonth month = SolarMonth.fromYm(year, this.month).next(months);
int y = month.getYear();
int m = month.getMonth();
int d = day;
if (1582 == y && 10 == m) {
if (d > 4 && d < 15) {
d += 10;
}
} else {
int maxDay = SolarUtil.getDaysOfMonth(y, m);
if (d > maxDay) {
d = maxDay;
}
}
return fromYmdHms(y, m, d, hour, minute, second);
}
/**
* 获取往后推几天的阳历日期,如果要往前推,则天数用负数
*
* @param days 天数
* @return 阳历日期
*/
public Solar next(int days) {
int y = year;
int m = month;
int d = day;
if (1582 == y && 10 == m) {
if (d > 4) {
d -= 10;
}
}
if (days > 0) {
d += days;
int daysInMonth = SolarUtil.getDaysOfMonth(y, m);
while (d > daysInMonth) {
d -= daysInMonth;
m++;
if (m > 12) {
m = 1;
y++;
}
daysInMonth = SolarUtil.getDaysOfMonth(y, m);
}
} else if (days < 0) {
while (d + days <= 0) {
m--;
if (m < 1) {
m = 12;
y--;
}
d += SolarUtil.getDaysOfMonth(y, m);
}
d += days;
}
if (1582 == y && 10 == m) {
if (d > 4) {
d += 10;
}
}
return fromYmdHms(y, m, d, hour, minute, second);
}
/**
* 取往后推几天的阳历日期,如果要往前推,则天数用负数
*
* @param days 天数
* @param onlyWorkday 是否仅限工作日
* @return 阳历日期
*/
public Solar next(int days, boolean onlyWorkday) {
if (!onlyWorkday) {
return next(days);
}
Solar solar = fromYmdHms(year, month, day, hour, minute, second);
if (days != 0) {
int rest = Math.abs(days);
int add = days < 0 ? -1 : 1;
while (rest > 0) {
solar = solar.next(add);
boolean work = true;
Holiday holiday = HolidayUtil.getHoliday(solar.getYear(), solar.getMonth(), solar.getDay());
if (null == holiday) {
int week = solar.getWeek();
if (0 == week || 6 == week) {
work = false;
}
} else {
work = holiday.isWork();
}
if (work) {
rest -= 1;
}
}
}
return solar;
}
/**
* 小时推移
*
* @param hours 小时数
* @return 阳历
*/
public Solar nextHour(int hours) {
int h = hour + hours;
int n = h < 0 ? -1 : 1;
int hour = Math.abs(h);
int days = hour / 24 * n;
hour = (hour % 24) * n;
if (hour < 0) {
hour += 24;
days--;
}
Solar solar = next(days);
return fromYmdHms(solar.getYear(), solar.getMonth(), solar.getDay(), hour, solar.getMinute(), solar.getSecond());
}
/**
* 获取薪资比例(感谢 https://gitee.com/smr1987)
*
* @return 薪资比例1/2/3
*/
public int getSalaryRate() {
// 元旦节
if (month == 1 && day == 1) {
return 3;
}
// 劳动节
if (month == 5 && day == 1) {
return 3;
}
// 国庆
if (month == 10 && day >= 1 && day <= 3) {
return 3;
}
Lunar lunar = getLunar();
// 春节
if (lunar.getMonth() == 1 && lunar.getDay() >= 1 && lunar.getDay() <= 3) {
return 3;
}
// 端午
if (lunar.getMonth() == 5 && lunar.getDay() == 5) {
return 3;
}
// 中秋
if (lunar.getMonth() == 8 && lunar.getDay() == 15) {
return 3;
}
// 清明
if ("清明".equals(lunar.getJieQi())) {
return 3;
}
Holiday holiday = HolidayUtil.getHoliday(year, month, day);
if (null != holiday) {
// 法定假日非上班
if (!holiday.isWork()) {
return 2;
}
} else {
// 周末
int week = getWeek();
if (week == 6 || week == 0) {
return 2;
}
}
// 工作日
return 1;
}
}