tradecatlabs's picture
deploy FateCat HF Space free web workbench
acb2cec verified
Raw
History Blame Contribute Delete
14.9 kB
# -*- coding: utf-8 -*-
from datetime import datetime
from math import ceil
from .util import SolarUtil, LunarUtil, HolidayUtil
class Solar:
"""
阳历日期
"""
# 2000年儒略日数(2000-1-1 12:00:00 UTC)
J2000 = 2451545
def __init__(self, year, month, day, hour, minute, second):
if year == 1582 and month == 10:
if 4 < day < 15:
raise Exception("wrong solar year %d month %d day %d" % (year, month, day))
if month < 1 or month > 12:
raise Exception("wrong month %d" % month)
if day < 1 or month > 31:
raise Exception("wrong day %d" % day)
if hour < 0 or hour > 23:
raise Exception("wrong hour %d" % hour)
if minute < 0 or minute > 59:
raise Exception("wrong minute %d" % minute)
if second < 0 or second > 59:
raise Exception("wrong second %d" % second)
self.__year = year
self.__month = month
self.__day = day
self.__hour = hour
self.__minute = minute
self.__second = second
@staticmethod
def fromDate(date):
return Solar(date.year, date.month, date.day, date.hour, date.minute, date.second)
@staticmethod
def fromJulianDay(julian_day):
d = int(julian_day + 0.5)
f = julian_day + 0.5 - d
if d >= 2299161:
c = int((d - 1867216.25) / 36524.25)
d += 1 + c - int(c / 4)
d += 1524
year = int((d - 122.1) / 365.25)
d -= int(365.25 * year)
month = int(d / 30.601)
d -= int(30.601 * month)
day = d
if month > 13:
month -= 13
year -= 4715
else:
month -= 1
year -= 4716
f *= 24
hour = int(f)
f -= hour
f *= 60
minute = int(f)
f -= minute
f *= 60
second = int(round(f))
if second > 59:
second -= 60
minute += 1
if minute > 59:
minute -= 60
hour += 1
if hour > 23:
hour -= 24
day += 1
return Solar(year, month, day, hour, minute, second)
@staticmethod
def fromYmdHms(year, month, day, hour, minute, second):
return Solar(year, month, day, hour, minute, second)
@staticmethod
def fromYmd(year, month, day):
return Solar(year, month, day, 0, 0, 0)
@staticmethod
def fromBaZi(year_gan_zhi, month_gan_zhi, day_gan_zhi, time_gan_zhi, sect=2, base_year=1900):
from . import Lunar
sect = 1 if 1 == sect else 2
solar_list = []
# 月地支距寅月的偏移值
m = LunarUtil.find(month_gan_zhi[1:], LunarUtil.ZHI, -1) - 2
if m < 0:
m += 12
# 月天干要一致
if ((LunarUtil.find(year_gan_zhi[:1], LunarUtil.GAN, -1) + 1) * 2 + m) % 10 != LunarUtil.find(month_gan_zhi[:1], LunarUtil.GAN, -1):
return solar_list
# 1年的立春是辛酉,序号57
y = LunarUtil.getJiaZiIndex(year_gan_zhi) - 57
if y < 0:
y += 60
y += 1
# 节令偏移值
m *= 2
# 时辰地支转时刻,子时按零点算
h = LunarUtil.find(time_gan_zhi[1:], LunarUtil.ZHI, -1) * 2
hours = [h]
if 0 == h and 2 == sect:
hours.append(23)
start_year = base_year - 1
# 结束年
end_year = datetime.now().year
while y <= end_year:
if y >= start_year:
# 立春为寅月的开始
jie_qi_table = Lunar.fromYmd(y, 1, 1).getJieQiTable()
# 节令推移,年干支和月干支就都匹配上了
solar_time = jie_qi_table[Lunar.JIE_QI_IN_USE[4 + m]]
if solar_time.getYear() >= base_year:
# 日干支和节令干支的偏移值
d = LunarUtil.getJiaZiIndex(day_gan_zhi) - LunarUtil.getJiaZiIndex(solar_time.getLunar().getDayInGanZhiExact2())
if d < 0:
d += 60
if d > 0:
# 从节令推移天数
solar_time = solar_time.next(d)
for hour in hours:
mi = 0
s = 0
if d == 0 and hour == solar_time.getHour():
# 如果正好是节令当天,且小时和节令的小时数相等的极端情况,把分钟和秒钟带上
mi = solar_time.getMinute()
s = solar_time.getSecond()
# 验证一下
solar = Solar.fromYmdHms(solar_time.getYear(), solar_time.getMonth(), solar_time.getDay(), hour, mi, s)
if d == 30:
solar = solar.nextHour(-1)
lunar = solar.getLunar()
dgz = lunar.getDayInGanZhiExact2() if 2 == sect else lunar.getDayInGanZhiExact()
if lunar.getYearInGanZhiExact() == year_gan_zhi and lunar.getMonthInGanZhiExact() == month_gan_zhi and dgz == day_gan_zhi and lunar.getTimeInGanZhi() == time_gan_zhi:
solar_list.append(solar)
y += 60
return solar_list
def isLeapYear(self):
"""
是否闰年
:return: True/False 闰年/非闰年
"""
return SolarUtil.isLeapYear(self.__year)
def getWeek(self):
"""
获取星期,0代表周日,1代表周一
:return: 0123456
"""
return (int(self.getJulianDay() + 0.5) + 7000001) % 7
def getWeekInChinese(self):
"""
获取星期的中文
:return: 日一二三四五六
"""
return SolarUtil.WEEK[self.getWeek()]
def getFestivals(self):
"""
获取节日,有可能一天会有多个节日
:return: 劳动节等
"""
festivals = []
key = "%d-%d" % (self.__month, self.__day)
if key in SolarUtil.FESTIVAL:
festivals.append(SolarUtil.FESTIVAL[key])
week = self.getWeek()
key = "%d-%d-%d" % (self.__month, int(ceil(self.__day / 7.0)), week)
if key in SolarUtil.WEEK_FESTIVAL:
festivals.append(SolarUtil.WEEK_FESTIVAL[key])
if self.__day + 7 > SolarUtil.getDaysOfMonth(self.__year, self.__month):
key = "%d-0-%d" % (self.__month, week)
if key in SolarUtil.WEEK_FESTIVAL:
festivals.append(SolarUtil.WEEK_FESTIVAL[key])
return festivals
def getOtherFestivals(self):
"""
获取非正式的节日,有可能一天会有多个节日
:return: 非正式的节日列表,如中元节
"""
festivals = []
key = "%d-%d" % (self.__month, self.__day)
if key in SolarUtil.OTHER_FESTIVAL:
for f in SolarUtil.OTHER_FESTIVAL[key]:
festivals.append(f)
return festivals
def getXingZuo(self):
"""
获取星座
:return: 星座
"""
index = 11
y = self.__month * 100 + self.__day
if 321 <= y <= 419:
index = 0
elif 420 <= y <= 520:
index = 1
elif 521 <= y <= 621:
index = 2
elif 622 <= y <= 722:
index = 3
elif 723 <= y <= 822:
index = 4
elif 823 <= y <= 922:
index = 5
elif 923 <= y <= 1023:
index = 6
elif 1024 <= y <= 1122:
index = 7
elif 1123 <= y <= 1221:
index = 8
elif y >= 1222 or y <= 119:
index = 9
elif y <= 218:
index = 10
return SolarUtil.XING_ZUO[index]
def getJulianDay(self):
"""
获取儒略日
:return: 儒略日
"""
y = self.__year
m = self.__month
d = self.__day + ((self.__second / 60.0 + self.__minute) / 60 + self.__hour) / 24
n = 0
g = False
if y * 372 + m * 31 + int(d) >= 588829:
g = True
if m <= 2:
m += 12
y -= 1
if g:
n = int(y / 100)
n = 2 - n + int(n / 4)
return int(365.25 * (y + 4716)) + int(30.6001 * (m + 1)) + d + n - 1524.5
def getLunar(self):
"""
获取农历
:return: 农历
"""
from .Lunar import Lunar
return Lunar.fromSolar(self)
def nextDay(self, days):
y = self.__year
m = self.__month
d = self.__day
if 1582 == y and 10 == m:
if d > 4:
d -= 10
if days > 0:
d += days
days_in_month = SolarUtil.getDaysOfMonth(y, m)
while d > days_in_month:
d -= days_in_month
m += 1
if m > 12:
m = 1
y += 1
days_in_month = SolarUtil.getDaysOfMonth(y, m)
elif days < 0:
while d + days <= 0:
m -= 1
if m < 1:
m = 12
y -= 1
d += SolarUtil.getDaysOfMonth(y, m)
d += days
if 1582 == y and 10 == m:
if d > 4:
d += 10
return Solar.fromYmdHms(y, m, d, self.__hour, self.__minute, self.__second)
def next(self, days, only_work_day=False):
"""
获取往后推几天的阳历日期,如果要往前推,则天数用负数
:param days: 天数
:param only_work_day: 是否仅工作日
:return: 阳历日期
"""
if not only_work_day:
return self.nextDay(days)
solar = Solar.fromYmdHms(self.__year, self.__month, self.__day, self.__hour, self.__minute, self.__second)
if days != 0:
rest = abs(days)
add = 1
if days < 0:
add = -1
while rest > 0:
solar = solar.next(add)
work = True
holiday = HolidayUtil.getHoliday(solar.getYear(), solar.getMonth(), solar.getDay())
if holiday is None:
week = solar.getWeek()
if 0 == week or 6 == week:
work = False
else:
work = holiday.isWork()
if work:
rest -= 1
return solar
def getYear(self):
return self.__year
def getMonth(self):
return self.__month
def getDay(self):
return self.__day
def getHour(self):
return self.__hour
def getMinute(self):
return self.__minute
def getSecond(self):
return self.__second
def toYmd(self):
return "%04d-%02d-%02d" % (self.__year, self.__month, self.__day)
def toYmdHms(self):
return "%s %02d:%02d:%02d" % (self.toYmd(), self.__hour, self.__minute, self.__second)
def toFullString(self):
s = self.toYmdHms()
if self.isLeapYear():
s += " 闰年"
s += " 星期"
s += self.getWeekInChinese()
for f in self.getFestivals():
s += " (" + f + ")"
for f in self.getOtherFestivals():
s += " (" + f + ")"
s += " "
s += self.getXingZuo()
s += "座"
return s
def toString(self):
return self.toYmd()
def __str__(self):
return self.toString()
def subtract(self, solar):
return SolarUtil.getDaysBetween(solar.getYear(), solar.getMonth(), solar.getDay(), self.__year, self.__month, self.__day)
def subtractMinute(self, solar):
days = self.subtract(solar)
cm = self.__hour * 60 + self.__minute
sm = solar.getHour() * 60 + solar.getMinute()
m = cm - sm
if m < 0:
m += 1440
days -= 1
m += days * 1440
return m
def isAfter(self, solar):
if self.__year > solar.getYear():
return True
if self.__year < solar.getYear():
return False
if self.__month > solar.getMonth():
return True
if self.__month < solar.getMonth():
return False
if self.__day > solar.getDay():
return True
if self.__day < solar.getDay():
return False
if self.__hour > solar.getHour():
return True
if self.__hour < solar.getHour():
return False
if self.__minute > solar.getMinute():
return True
if self.__minute < solar.getMinute():
return False
return self.__second > solar.getSecond()
def isBefore(self, solar):
if self.__year > solar.getYear():
return False
if self.__year < solar.getYear():
return True
if self.__month > solar.getMonth():
return False
if self.__month < solar.getMonth():
return True
if self.__day > solar.getDay():
return False
if self.__day < solar.getDay():
return True
if self.__hour > solar.getHour():
return False
if self.__hour < solar.getHour():
return True
if self.__minute > solar.getMinute():
return False
if self.__minute < solar.getMinute():
return True
return self.__second < solar.getSecond()
def nextYear(self, years):
y = self.__year + years
m = self.__month
d = self.__day
if 1582 == y and 10 == m:
if 4 < d < 15:
d += 10
elif 2 == m:
if d > 28:
if not SolarUtil.isLeapYear(y):
d = 28
return Solar.fromYmdHms(y, m, d, self.__hour, self.__minute, self.__second)
def nextMonth(self, months):
from . import SolarMonth
month = SolarMonth.fromYm(self.__year, self.__month).next(months)
y = month.getYear()
m = month.getMonth()
d = self.__day
if 1582 == y and 10 == m:
if 4 < d < 15:
d += 10
else:
days = SolarUtil.getDaysOfMonth(y, m)
if d > days:
d = days
return Solar.fromYmdHms(y, m, d, self.__hour, self.__minute, self.__second)
def nextHour(self, hours):
h = self.__hour + hours
n = 1
if h < 0:
n = -1
hour = abs(h)
days = int(hour / 24) * n
hour = (hour % 24) * n
if hour < 0:
hour += 24
days -= 1
solar = self.next(days)
return Solar.fromYmdHms(solar.getYear(), solar.getMonth(), solar.getDay(), hour, solar.getMinute(), solar.getSecond())