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 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 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 fromBaZi(String yearGanZhi, String monthGanZhi, String dayGanZhi, String timeGanZhi, int sect, int baseYear) { sect = (1 == sect) ? 1 : 2; List l = new ArrayList(); // 月地支距寅月的偏移值 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 jieQiList = new ArrayList(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 getFestivals() { List l = new ArrayList(); //获取几月几日对应的节日 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 getOtherFestivals() { List l = new ArrayList(); List 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; } }