using System;
using System.Collections.Generic;
using System.Text;
using Lunar.Util;
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable IdentifierTypo
namespace Lunar
{
///
/// 阳历日期
///
public class Solar
{
///
/// 2000年儒略日数(2000-1-1 12:00:00 UTC)
///
public const double J2000 = 2451545;
///
/// 年
///
public int Year { get; }
///
/// 月
///
public int Month { get; }
///
/// 日
///
public int Day { get; }
///
/// 时
///
public int Hour { get; }
///
/// 分
///
public int Minute { get; }
///
/// 秒
///
public int Second { get; }
///
/// 默认使用当前日期初始化
///
public Solar(): this(DateTime.Now)
{
}
///
/// 通过年月日时分初始化
///
/// 年
/// 月,1到12
/// 日,1到31
/// 小时,0到23
/// 分钟,0到59
/// 秒钟,0到59
public Solar(int year, int month, int day, int hour = 0, int minute = 0, int second = 0)
{
if (1582 == year && 10 == month)
{
if (day > 4 && day < 15)
{
throw new ArgumentException($"wrong solar year {year} month {month} day {day}");
}
}
if (month < 1 || month > 12)
{
throw new ArgumentException($"wrong month {month}");
}
if (day < 1 || day > 31)
{
throw new ArgumentException($"wrong day {day}");
}
if (hour < 0 || hour > 23)
{
throw new ArgumentException($"wrong hour {hour}");
}
if (minute < 0 || minute > 59)
{
throw new ArgumentException($"wrong minute {minute}");
}
if (second < 0 || second > 59)
{
throw new ArgumentException($"wrong second {second}");
}
Year = year;
Month = month;
Day = day;
Hour = hour;
Minute = minute;
Second = second;
}
///
/// 通过日期初始化
///
/// 日期
public Solar(DateTime date): this(date.Year, date.Month, date.Day, date.Hour, date.Minute,date.Second)
{
}
///
/// 通过儒略日初始化
///
/// 儒略日
public Solar(double julianDay)
{
var d = (int)(julianDay + 0.5);
var f = julianDay + 0.5 - d;
if (d >= 2299161)
{
var c = (int)((d - 1867216.25) / 36524.25);
d += 1 + c - (int)(c * 1D / 4);
}
d += 1524;
var year = (int)((d - 122.1) / 365.25);
d -= (int)(365.25 * year);
var month = (int)(d * 1D / 30.601);
d -= (int)(30.601 * month);
var day = d;
if (month > 13)
{
month -= 13;
year -= 4715;
}
else
{
month -= 1;
year -= 4716;
}
f *= 24;
var hour = (int)f;
f -= hour;
f *= 60;
var minute = (int)f;
f -= minute;
f *= 60;
var second = (int)Math.Round(f);
if (second > 59)
{
second -= 60;
minute++;
}
if (minute > 59)
{
minute -= 60;
hour++;
}
if (hour > 23)
{
hour -= 24;
day += 1;
}
Year = year;
Month = month;
Day = day;
Hour = hour;
Minute = minute;
Second = second;
}
///
/// 通过指定年月日时分获取阳历
///
/// 年
/// 月,1到12
/// 日,1到31
/// 小时,0到23
/// 分钟,0到59
/// 秒钟,0到59
/// 阳历
public static Solar FromYmdHms(int year, int month, int day, int hour = 0, int minute = 0,int second = 0)
{
return new Solar(year, month, day, hour, minute,second);
}
///
/// 通过指定日期获取阳历
///
/// 日期
/// 阳历
public static Solar FromDate(DateTime date)
{
return new Solar(date);
}
///
/// 通过指定儒略日获取阳历
///
/// 儒略日
/// 阳历
public static Solar FromJulianDay(double julianDay)
{
return new Solar(julianDay);
}
///
/// 通过八字获取阳历列表
///
/// 年柱
/// 月柱
/// 日柱
/// 时柱
/// 流派,2晚子时日柱按当天,1晚子时日柱按明天
/// 起始年
/// 符合的阳历列表
public static List FromBaZi(string yearGanZhi, string monthGanZhi, string dayGanZhi, string timeGanZhi,
int sect = 2, int baseYear = 1900)
{
sect = (1 == sect) ? 1 : 2;
var l = new List();
// 月地支距寅月的偏移值
var 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
var y = LunarUtil.GetJiaZiIndex(yearGanZhi) - 57;
if (y < 0)
{
y += 60;
}
y++;
// 节令偏移值
m *= 2;
// 时辰地支转时刻,子时按零点算
var h = LunarUtil.find(timeGanZhi.Substring(1), LunarUtil.ZHI, -1) * 2;
int[] hours = { h };
if (0 == h && 2 == sect)
{
hours = new [] { 0, 23 };
}
var startYear = baseYear - 1;
// 结束年
var endYear = DateTime.Now.Year;
while (y <= endYear)
{
if (y >= startYear)
{
// 立春为寅月的开始
var solarTime = Lunar.FromYmdHms(y, 1, 1).JieQiTable[Lunar.JIE_QI_IN_USE[4 + m]];
// 节令推移,年干支和月干支就都匹配上了
if (solarTime.Year >= baseYear)
{
// 日干支和节令干支的偏移值
var d = LunarUtil.GetJiaZiIndex(dayGanZhi) - LunarUtil.GetJiaZiIndex(solarTime.Lunar.DayInGanZhiExact2);
if (d < 0)
{
d += 60;
}
if (d > 0)
{
// 从节令推移天数
solarTime = solarTime.Next(d);
}
foreach (var hour in hours)
{
var mi = 0;
var s = 0;
if (d == 0 && hour == solarTime.Hour)
{
// 如果正好是节令当天,且小时和节令的小时数相等的极端情况,把分钟和秒钟带上
mi = solarTime.Minute;
s = solarTime.Second;
}
// 验证一下
var solar = FromYmdHms(solarTime.Year, solarTime.Month, solarTime.Day, hour, mi, s);
var lunar = solar.Lunar;
var dgz = (2 == sect) ? lunar.DayInGanZhiExact2 : lunar.DayInGanZhiExact;
if (lunar.YearInGanZhiExact.Equals(yearGanZhi) &&
lunar.MonthInGanZhiExact.Equals(monthGanZhi) && dgz.Equals(dayGanZhi) &&
lunar.TimeInGanZhi.Equals(timeGanZhi))
{
l.Add(solar);
}
}
}
}
y += 60;
}
return l;
}
///
/// 是否闰年
///
public bool LeapYear => SolarUtil.IsLeapYear(Year);
///
/// 星期,0代表周日,1代表周一
///
public int Week => ((int)(JulianDay + 0.5) + 7000001) % 7;
///
/// 星期的中文:日一二三四五六
///
public string WeekInChinese => SolarUtil.WEEK[Week];
///
/// 星座
///
public string XingZuo
{
get
{
var index = 11;
var 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.XING_ZUO[index];
}
}
///
/// 节日,有可能一天会有多个节日
///
public List Festivals
{
get
{
var l = new List();
//获取几月几日对应的节日
try
{
l.Add(SolarUtil.FESTIVAL[Month + "-" + Day]);
}
catch
{
// ignored
}
//计算几月第几个星期几对应的节日
var weeks = (int)Math.Ceiling(Day / 7D);
//星期几,0代表星期天
try
{
l.Add(SolarUtil.WEEK_FESTIVAL[Month + "-" + weeks + "-" + Week]);
}
catch
{
// ignored
}
if (Day + 7 <= SolarUtil.GetDaysOfMonth(Year, Month)) return l;
try
{
l.Add(SolarUtil.WEEK_FESTIVAL[Month + "-0-" + Week]);
}
catch
{
// ignored
}
return l;
}
}
///
/// 非正式节日
///
public List OtherFestivals
{
get
{
var l = new List();
try
{
var fs = SolarUtil.OTHER_FESTIVAL[Month + "-" + Day];
l.AddRange(fs);
}
catch
{
// ignored
}
return l;
}
}
///
/// 阴历
///
public Lunar Lunar => new Lunar(this);
///
/// 儒略日
///
public double JulianDay
{
get
{
var y = Year;
var m = Month;
var d = Day + ((Second * 1D / 60 + Minute) / 60 + Hour) / 24;
var n = 0;
var 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;
}
}
///
public override string ToString()
{
return Ymd;
}
///
/// yyyy-MM-dd
///
public string Ymd
{
get
{
var y = Year + "";
while (y.Length < 4)
{
y = "0" + y;
}
return y + "-" + (Month < 10 ? "0" : "") + Month + "-" + (Day < 10 ? "0" : "") + Day;
}
}
///
/// yyyy-MM-dd HH:mm:ss
///
public string YmdHms => Ymd + " " + (Hour < 10 ? "0" : "") + Hour + ":" + (Minute < 10 ? "0" : "") + Minute + ":" + (Second < 10 ? "0" : "") + Second;
///
/// 完整字符串输出
///
public string FullString
{
get
{
var s = new StringBuilder();
s.Append(YmdHms);
if (LeapYear)
{
s.Append(' ');
s.Append("闰年");
}
s.Append(' ');
s.Append("星期");
s.Append(WeekInChinese);
s.Append(' ');
s.Append(XingZuo);
s.Append('座');
return s.ToString();
}
}
///
/// 阳历日期相减,获得相差天数
///
/// 阳历
/// 天数
public int Subtract(Solar solar)
{
return SolarUtil.GetDaysBetween(solar.Year, solar.Month, solar.Day, Year, Month, Day);
}
///
/// 阳历日期相减,获得相差分钟数
///
/// 阳历
/// 分钟数
public int SubtractMinute(Solar solar)
{
var days = Subtract(solar);
var cm = Hour * 60 + Minute;
var sm = solar.Hour * 60 + solar.Minute;
var m = cm - sm;
if (m < 0)
{
m += 1440;
days--;
}
m += days * 1440;
return m;
}
///
/// 是否在指定日期之后
///
/// 阳历
/// true/false
public bool IsAfter(Solar solar)
{
if (Year > solar.Year)
{
return true;
}
if (Year < solar.Year)
{
return false;
}
if (Month > solar.Month)
{
return true;
}
if (Month < solar.Month)
{
return false;
}
if (Day > solar.Day) {
return true;
}
if (Day < solar.Day)
{
return false;
}
if (Hour > solar.Hour)
{
return true;
}
if (Hour < solar.Hour)
{
return false;
}
if (Minute > solar.Minute)
{
return true;
}
if (Minute < solar.Minute)
{
return false;
}
return Second > solar.Second;
}
///
/// 是否在指定日期之前
///
/// 阳历
/// true/false
public bool IsBefore(Solar solar)
{
if (Year > solar.Year)
{
return false;
}
if (Year < solar.Year)
{
return true;
}
if (Month > solar.Month)
{
return false;
}
if (Month < solar.Month)
{
return true;
}
if (Day > solar.Day)
{
return false;
}
if (Day < solar.Day)
{
return true;
}
if (Hour > solar.Hour)
{
return false;
}
if (Hour < solar.Hour)
{
return true;
}
if (Minute > solar.Minute)
{
return false;
}
if (Minute < solar.Minute)
{
return true;
}
return Second < solar.Second;
}
///
/// 年推移
///
/// 年数
/// 阳历
public Solar NextYear(int years)
{
var y = Year + years;
var m = Month;
var 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 new Solar(y, m, d, Hour, Minute, Second);
}
///
/// 月推移
///
/// 月数
/// 阳历
public Solar NextMonth(int months)
{
var month = SolarMonth.FromYm(Year, Month).Next(months);
var y = month.Year;
var m = month.Month;
var d = Day;
if (1582 == y && 10 == m)
{
if (d > 4 && d < 15)
{
d += 10;
}
}
else
{
var maxDay = SolarUtil.GetDaysOfMonth(y, m);
if (d > maxDay)
{
d = maxDay;
}
}
return new Solar(y, m, d, Hour, Minute, Second);
}
///
/// 日推移
///
/// 天数
/// 阳历
public Solar NextDay(int days)
{
var y = Year;
var m = Month;
var d = Day;
if (1582 == y && 10 == m)
{
if (d > 4)
{
d -= 10;
}
}
if (days > 0)
{
d += days;
var 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 new Solar(y, m, d, Hour, Minute, Second);
}
///
/// 获取往后推几天的阳历日期,如果要往前推,则天数用负数
///
/// 天数
/// 是否仅工作日
/// 阳历日期
public Solar Next(int days, bool onlyWorkday = false)
{
if(!onlyWorkday)
{
return NextDay(days);
}
var solar = new Solar(Year, Month, Day, Hour, Minute, Second);
if (days != 0)
{
var rest = Math.Abs(days);
var add = days < 0 ? -1 : 1;
while (rest > 0)
{
solar = solar.Next(add);
var work = true;
var holiday = HolidayUtil.GetHoliday(solar.Year, solar.Month, solar.Day);
if (null == holiday)
{
var week = solar.Week;
if (0 == week || 6 == week) {
work = false;
}
} else
{
work = holiday.Work;
}
if (work)
{
rest -= 1;
}
}
}
return solar;
}
///
/// 小时推移
///
/// 小时数
/// 阳历
public Solar NextHour(int hours)
{
var h = Hour + hours;
var n = h < 0 ? -1 : 1;
var hour = Math.Abs(h);
var days = hour / 24 * n;
hour = (hour % 24) * n;
if (hour < 0)
{
hour += 24;
days--;
}
var solar = Next(days);
return new Solar(solar.Year, solar.Month, solar.Day, hour, solar.Minute, solar.Second);
}
///
/// 获取薪资比例(感谢 https://gitee.com/smr1987)
///
/// 薪资比例:1/2/3
public int GetSalaryRate()
{
switch (Month)
{
// 元旦节
case 1 when Day == 1:
// 劳动节
case 5 when Day == 1:
// 国庆
case 10 when Day >= 1 && Day <= 3:
return 3;
}
var lunarMonth = Lunar.Month;
var lunarDay = Lunar.Day;
switch (lunarMonth)
{
// 春节
case 1 when lunarDay >= 1 && lunarDay <= 3:
// 端午
case 5 when lunarDay == 5:
// 中秋
case 8 when lunarDay == 15:
return 3;
}
// 清明
if ("清明".Equals(Lunar.JieQi)) {
return 3;
}
var holiday = HolidayUtil.GetHoliday(Year, Month, Day);
if (null != holiday) {
// 法定假日非上班
if (!holiday.Work) {
return 2;
}
} else {
// 周末
var week = Week;
if (week == 6 || week == 0) {
return 2;
}
}
// 工作日
return 1;
}
}
}