Spaces:
Running
Running
| # -*- 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 | |
| def fromDate(date): | |
| return Solar(date.year, date.month, date.day, date.hour, date.minute, date.second) | |
| 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) | |
| def fromYmdHms(year, month, day, hour, minute, second): | |
| return Solar(year, month, day, hour, minute, second) | |
| def fromYmd(year, month, day): | |
| return Solar(year, month, day, 0, 0, 0) | |
| 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()) | |