副本编号 LY-20261007-AC63(水印指纹已嵌入) 含 README、.gitignore;排除 .DS_Store / __pycache__ / .pytest_cache
919 lines
31 KiB
Python
919 lines
31 KiB
Python
# -*- coding:utf-8 -*-
|
|
from __future__ import annotations
|
|
|
|
from math import floor, ceil
|
|
from typing import TYPE_CHECKING, Union, List
|
|
|
|
from tyme4py import LoopTyme, AbstractCultureDay
|
|
from tyme4py.culture import Week, Constellation, PhaseDay, Phase
|
|
from tyme4py.culture.dog import DogDay, Dog
|
|
from tyme4py.culture.nine import NineDay, Nine
|
|
from tyme4py.culture.phenology import PhenologyDay, Phenology
|
|
from tyme4py.culture.plumrain import PlumRainDay, PlumRain
|
|
from tyme4py.culture.star.nine import NineStar
|
|
from tyme4py.enums import HideHeavenStemType
|
|
from tyme4py.event import Event
|
|
from tyme4py.unit import YearUnit, MonthUnit, WeekUnit, DayUnit, SecondUnit
|
|
from tyme4py.util import ShouXingUtil
|
|
from tyme4py.jd import JulianDay
|
|
from tyme4py.festival import SolarFestival
|
|
from tyme4py.holiday import LegalHoliday
|
|
|
|
if TYPE_CHECKING:
|
|
from tyme4py.lunar import LunarDay, LunarMonth, LunarHour
|
|
from tyme4py.sixtycycle import HideHeavenStemDay, SixtyCycleDay, SixtyCycleHour
|
|
from tyme4py.rabbyung import RabByungYear, RabByungDay
|
|
from tyme4py.hijri import HijriDay
|
|
|
|
|
|
class SolarTerm(LoopTyme):
|
|
"""节气"""
|
|
NAMES: List[str] = ['冬至', '小寒', '大寒', '立春', '雨水', '惊蛰', '春分', '清明', '谷雨', '立夏', '小满', '芒种', '夏至', '小暑', '大暑', '立秋', '处暑', '白露', '秋分', '寒露', '霜降', '立冬', '小雪', '大雪']
|
|
"""名称"""
|
|
|
|
def __init__(self, year: int, index_or_name: Union[int, str]):
|
|
super().__init__(self.NAMES, index_or_name)
|
|
y = year
|
|
if isinstance(index_or_name, int):
|
|
size = self.NAMES.__len__()
|
|
y = (year * size + index_or_name) // size
|
|
jd = floor((y - 2000) * 365.2422 + 180)
|
|
# 355对应2000.12冬至,计算粗略估计值
|
|
w = floor((jd - 355 + 183) / 365.2422) * 365.2422 + 355
|
|
if ShouXingUtil.calc_qi(w) > jd:
|
|
w -= 365.2422
|
|
self._year = y
|
|
self._cursory_julian_day = ShouXingUtil.calc_qi(w + 15.2184 * self.get_index())
|
|
|
|
@classmethod
|
|
def from_index(cls, year: int, index: int) -> SolarTerm:
|
|
return cls(year, index)
|
|
|
|
@classmethod
|
|
def from_name(cls, year: int, name: str) -> SolarTerm:
|
|
return cls(year, name)
|
|
|
|
def next(self, n: int) -> SolarTerm:
|
|
size = self.get_size()
|
|
i = self.get_index() + n
|
|
return SolarTerm((self._year * size + i) // size, self._index_of_by(i))
|
|
|
|
def is_jie(self) -> bool:
|
|
"""是否节令"""
|
|
return self._index % 2 == 1
|
|
|
|
def is_qi(self) -> bool:
|
|
"""是否气令"""
|
|
return self._index % 2 == 0
|
|
|
|
def get_julian_day(self) -> JulianDay:
|
|
"""
|
|
儒略日(精确到秒)
|
|
:return : 儒略日
|
|
"""
|
|
return JulianDay.from_julian_day(ShouXingUtil.qi_accurate2(self._cursory_julian_day) + JulianDay.J2000)
|
|
|
|
def get_solar_day(self) -> SolarDay:
|
|
"""
|
|
公历日(用于日历)
|
|
@return: 公历日
|
|
"""
|
|
return JulianDay.from_julian_day(self._cursory_julian_day + JulianDay.J2000).get_solar_day()
|
|
|
|
def get_year(self) -> int:
|
|
return self._year
|
|
|
|
def get_cursory_julian_day(self) -> float:
|
|
"""
|
|
儒略日(用于日历,只精确到日中午12:00)
|
|
@return: 儒略日
|
|
"""
|
|
return self._cursory_julian_day
|
|
|
|
|
|
class SolarTermDay(AbstractCultureDay):
|
|
"""节气第几天"""
|
|
|
|
def __init__(self, solar_term: SolarTerm, day_index: int):
|
|
super().__init__(solar_term, day_index)
|
|
|
|
def get_solar_term(self) -> SolarTerm:
|
|
"""
|
|
:return: 节气
|
|
"""
|
|
return self.get_culture()
|
|
|
|
|
|
class SolarYear(YearUnit):
|
|
"""公历年"""
|
|
|
|
def __init__(self, year: int):
|
|
SolarYear.validate(year)
|
|
super().__init__(year)
|
|
|
|
@staticmethod
|
|
def validate(year: int) -> None:
|
|
if year < 1 or year > 9999:
|
|
raise ValueError(f'illegal solar year: {year}')
|
|
|
|
@classmethod
|
|
def from_year(cls, year: int) -> SolarYear:
|
|
return cls(year)
|
|
|
|
def get_day_count(self) -> int:
|
|
if self._year == 1582:
|
|
return 355
|
|
return 366 if self.is_leap() else 365
|
|
|
|
def is_leap(self) -> bool:
|
|
if self._year < 1600:
|
|
return self._year % 4 == 0
|
|
return (self._year % 4 == 0 and self._year % 100 != 0) or (self._year % 400 == 0)
|
|
|
|
def get_name(self) -> str:
|
|
return f"{self._year}年"
|
|
|
|
def next(self, n: int) -> SolarYear:
|
|
return SolarYear.from_year(self._year + n)
|
|
|
|
def get_months(self) -> List[SolarMonth]:
|
|
return [SolarMonth.from_ym(self._year, month) for month in range(1, 13)]
|
|
|
|
def get_seasons(self) -> List[SolarSeason]:
|
|
return [SolarSeason.from_index(self._year, i) for i in range(4)]
|
|
|
|
def get_half_years(self) -> List[SolarHalfYear]:
|
|
return [SolarHalfYear.from_index(self._year, i) for i in range(2)]
|
|
|
|
def get_rab_byung_year(self) -> RabByungYear:
|
|
from tyme4py.rabbyung import RabByungYear
|
|
return RabByungYear.from_year(self._year)
|
|
|
|
|
|
class SolarHalfYear(YearUnit):
|
|
"""公历半年"""
|
|
NAMES: List[str] = ['上半年', '下半年']
|
|
|
|
@staticmethod
|
|
def validate(year: int, index: int) -> None:
|
|
if index < 0 or index > 1:
|
|
raise ValueError(f'illegal solar half year index: {index}')
|
|
SolarYear.validate(year)
|
|
|
|
def __init__(self, year: int, index: int):
|
|
SolarHalfYear.validate(year, index)
|
|
super().__init__(year)
|
|
self._index = index
|
|
|
|
@classmethod
|
|
def from_index(cls, year: int, index: int) -> SolarHalfYear:
|
|
return cls(year, index)
|
|
|
|
def get_solar_year(self) -> SolarYear:
|
|
return SolarYear.from_year(self._year)
|
|
|
|
def get_index(self) -> int:
|
|
return self._index
|
|
|
|
def get_name(self) -> str:
|
|
return self.NAMES[self._index]
|
|
|
|
def __str__(self) -> str:
|
|
return f'{self.get_solar_year()}{self.get_name()}'
|
|
|
|
def next(self, n: int) -> SolarHalfYear:
|
|
i = self._index + n
|
|
return SolarHalfYear.from_index((self.get_year() * 2 + i) // 2, self.index_of(i, 2))
|
|
|
|
def get_months(self) -> List[SolarMonth]:
|
|
y = self.get_year()
|
|
return [SolarMonth.from_ym(y, self._index * 6 + i) for i in range(1, 7)]
|
|
|
|
def get_seasons(self) -> List[SolarSeason]:
|
|
y = self.get_year()
|
|
return [SolarSeason.from_index(y, self._index * 2 + i) for i in range(2)]
|
|
|
|
|
|
class SolarSeason(YearUnit):
|
|
"""公历季度"""
|
|
NAMES: List[str] = ['一季度', '二季度', '三季度', '四季度']
|
|
|
|
@staticmethod
|
|
def validate(year: int, index: int) -> None:
|
|
if index < 0 or index > 3:
|
|
raise ValueError(f'illegal solar season index: {index}')
|
|
SolarYear.validate(year)
|
|
|
|
def __init__(self, year: int, index: int):
|
|
SolarSeason.validate(year, index)
|
|
super().__init__(year)
|
|
self._index = index
|
|
|
|
@classmethod
|
|
def from_index(cls, year: int, index: int) -> SolarSeason:
|
|
return cls(year, index)
|
|
|
|
def get_solar_year(self) -> SolarYear:
|
|
return SolarYear.from_year(self._year)
|
|
|
|
def get_index(self) -> int:
|
|
return self._index
|
|
|
|
def get_name(self) -> str:
|
|
return self.NAMES[self._index]
|
|
|
|
def __str__(self) -> str:
|
|
return f'{self.get_solar_year()}{self.get_name()}'
|
|
|
|
def next(self, n: int) -> SolarSeason:
|
|
i = self._index + n
|
|
return SolarSeason.from_index((self.get_year() * 4 + i) // 4, self.index_of(i, 4))
|
|
|
|
def get_months(self) -> List[SolarMonth]:
|
|
y = self.get_year()
|
|
return [SolarMonth.from_ym(y, self._index * 3 + i) for i in range(1, 4)]
|
|
|
|
|
|
class SolarMonth(MonthUnit):
|
|
"""公历月"""
|
|
NAMES: List[str] = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
|
|
_DAYS: List[int] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
|
"""每月天数"""
|
|
|
|
@staticmethod
|
|
def validate(year: int, month: int) -> None:
|
|
if month < 1 or month > 12:
|
|
raise ValueError(f'illegal solar month: {month}')
|
|
SolarYear.validate(year)
|
|
|
|
def __init__(self, year: int, month: int):
|
|
"""
|
|
:param year: 年
|
|
:param month: 月
|
|
"""
|
|
SolarMonth.validate(year, month)
|
|
super().__init__(year, month)
|
|
|
|
@classmethod
|
|
def from_ym(cls, year: int, month: int) -> SolarMonth:
|
|
return cls(year, month)
|
|
|
|
def get_solar_year(self) -> SolarYear:
|
|
"""
|
|
:return: 公历年
|
|
"""
|
|
return SolarYear.from_year(self._year)
|
|
|
|
def get_day_count(self) -> int:
|
|
"""
|
|
当月的总天数
|
|
:return: 天数(1582年10月只有21天)
|
|
"""
|
|
if 1582 == self._year and 10 == self._month:
|
|
return 21
|
|
d: int = self._DAYS[self.get_index_in_year()]
|
|
# 公历闰年2月多一天
|
|
if 2 == self._month and self.get_solar_year().is_leap():
|
|
d += 1
|
|
return d
|
|
|
|
def get_index_in_year(self) -> int:
|
|
"""
|
|
位于当年的月索引
|
|
:return: 位于当年的索引(0-11)
|
|
"""
|
|
return self._month - 1
|
|
|
|
def get_season(self) -> SolarSeason:
|
|
"""
|
|
:return: 公历季度 SolarSeason
|
|
"""
|
|
return SolarSeason(self._year, int(self.get_index_in_year() / 3))
|
|
|
|
def get_week_count(self, start: int) -> int:
|
|
"""
|
|
当月有几周
|
|
:param start:起始星期,1234560分别代表星期一至星期天
|
|
:return:周数
|
|
"""
|
|
return ceil((self.index_of(SolarDay(self._year, self._month, 1).get_week().get_index() - start, 7) + self.get_day_count()) / 7)
|
|
|
|
def get_name(self) -> str:
|
|
return self.NAMES[self.get_index_in_year()]
|
|
|
|
def __str__(self) -> str:
|
|
return f'{self.get_solar_year()}{self.get_name()}'
|
|
|
|
def next(self, n: int) -> SolarMonth:
|
|
i: int = self._month - 1 + n
|
|
return SolarMonth((self._year * 12 + i) // 12, self.index_of(i, 12) + 1)
|
|
|
|
def get_weeks(self, start: int) -> List[SolarWeek]:
|
|
"""
|
|
本月的公历周列表
|
|
:param start: 星期几作为一周的开始,1234560分别代表星期一至星期天
|
|
:return: 周列表
|
|
"""
|
|
l: List[SolarWeek] = []
|
|
for i in range(0, self.get_week_count(start)):
|
|
l.append(SolarWeek(self._year, self._month, i, start))
|
|
return l
|
|
|
|
def get_days(self) -> List[SolarDay]:
|
|
"""
|
|
本月的公历日列表
|
|
:return: 公历日 SolarDay 的列表,从1日开始
|
|
"""
|
|
l: List[SolarDay] = []
|
|
for i in range(1, self.get_day_count()):
|
|
l.append(SolarDay(self._year, self._month, i))
|
|
return l
|
|
|
|
def get_first_day(self) -> SolarDay:
|
|
"""
|
|
本月第一天
|
|
:return: 公历日 SolarDay
|
|
"""
|
|
return SolarDay.from_ymd(self._year, self._month, 1)
|
|
|
|
|
|
class SolarWeek(WeekUnit):
|
|
"""公历周"""
|
|
|
|
@staticmethod
|
|
def validate(year: int, month: int, index: int, start: int) -> None:
|
|
WeekUnit.validate(year, month, index, start)
|
|
m: SolarMonth = SolarMonth(year, month)
|
|
if index >= m.get_week_count(start):
|
|
raise ValueError(f'illegal solar week index: {index} in month: {m}')
|
|
|
|
def __init__(self, year: int, month: int, index: int, start: int):
|
|
"""
|
|
:param year: 年
|
|
:param month: 月
|
|
:param index: 索引,0-5
|
|
:param start: 起始星期,1234560分别代表星期一至星期天
|
|
"""
|
|
SolarWeek.validate(year, month, index, start)
|
|
super().__init__(year, month, index, start)
|
|
|
|
@classmethod
|
|
def from_ym(cls, year: int, month: int, index: int, start: int) -> SolarWeek:
|
|
return cls(year, month, index, start)
|
|
|
|
def get_solar_month(self) -> SolarMonth:
|
|
"""
|
|
:return: 公历月
|
|
"""
|
|
return SolarMonth.from_ym(self._year, self._month)
|
|
|
|
def get_index_in_year(self) -> int:
|
|
"""
|
|
位于当年的索引
|
|
:return: 索引 注意:索引值是从0开始,即0代表第一周
|
|
"""
|
|
i: int = 0
|
|
# 本周第1天
|
|
first_day: SolarDay = self.get_first_day()
|
|
# 今年第1周
|
|
w: SolarWeek = SolarWeek(self._year, 1, 0, self._start)
|
|
while not w.get_first_day() == first_day:
|
|
w = w.next(1)
|
|
i += 1
|
|
return i
|
|
|
|
def get_name(self) -> str:
|
|
return self.NAMES[self._index]
|
|
|
|
def __str__(self) -> str:
|
|
return f'{self.get_solar_month()}{self.get_name()}'
|
|
|
|
def next(self, n: int) -> SolarWeek:
|
|
d: int = self._index + n
|
|
m: SolarMonth = self.get_solar_month()
|
|
if n > 0:
|
|
week_count: int = m.get_week_count(self._start)
|
|
while d >= week_count:
|
|
d -= week_count
|
|
m = m.next(1)
|
|
if m.get_first_day().get_week().get_index() != self._start:
|
|
d += 1
|
|
week_count = m.get_week_count(self._start)
|
|
elif n < 0:
|
|
while d < 0:
|
|
if m.get_first_day().get_week().get_index() != self._start:
|
|
d -= 1
|
|
m = m.next(-1)
|
|
d += m.get_week_count(self._start)
|
|
return SolarWeek(m.get_year(), m.get_month(), d, self._start)
|
|
|
|
def get_first_day(self) -> SolarDay:
|
|
"""
|
|
本周第1天
|
|
:return: 公历日
|
|
"""
|
|
first_day: SolarDay = SolarDay(self._year, self._month, 1)
|
|
return first_day.next(self._index * 7 - self.index_of(first_day.get_week().get_index() - self._start, 7))
|
|
|
|
def get_days(self) -> List[SolarDay]:
|
|
"""
|
|
本周公历日列表
|
|
:return: 公历日列表
|
|
"""
|
|
l: List[SolarDay] = []
|
|
d: SolarDay = self.get_first_day()
|
|
l.append(d)
|
|
for i in range(1, 7):
|
|
l.append(d.next(i))
|
|
return l
|
|
|
|
def __eq__(self, o: SolarWeek) -> bool:
|
|
return o and o.get_first_day() == self.get_first_day()
|
|
|
|
|
|
class SolarDay(DayUnit):
|
|
"""
|
|
公历日
|
|
"""
|
|
NAMES: List[str] = ['1日', '2日', '3日', '4日', '5日', '6日', '7日', '8日', '9日', '10日', '11日', '12日', '13日', '14日', '15日', '16日', '17日', '18日', '19日', '20日', '21日', '22日', '23日', '24日', '25日', '26日', '27日', '28日', '29日', '30日', '31日']
|
|
|
|
@staticmethod
|
|
def validate(year: int, month: int, day: int) -> None:
|
|
if day < 1:
|
|
raise ValueError(f'illegal solar day: {year}-{month}-{day}')
|
|
if 1582 == year and 10 == month:
|
|
if (4 < day < 15) or day > 31:
|
|
raise ValueError(f'illegal solar day: {year}-{month}-{day}')
|
|
elif day > SolarMonth.from_ym(year, month).get_day_count():
|
|
raise ValueError(f'illegal solar day: {year}-{month}-{day}')
|
|
|
|
def __init__(self, year: int, month: int, day: int):
|
|
SolarDay.validate(year, month, day)
|
|
super().__init__(year, month, day)
|
|
|
|
@classmethod
|
|
def from_ymd(cls, year: int, month: int, day: int) -> SolarDay:
|
|
return cls(year, month, day)
|
|
|
|
def get_solar_month(self) -> SolarMonth:
|
|
"""
|
|
:return: 公历月
|
|
"""
|
|
return SolarMonth.from_ym(self._year, self._month)
|
|
|
|
def get_week(self) -> Week:
|
|
"""
|
|
:return: 星期
|
|
"""
|
|
return self.get_julian_day().get_week()
|
|
|
|
def get_constellation(self) -> Constellation:
|
|
"""
|
|
:return: 星座 Constellation
|
|
"""
|
|
m: int = self._month - 1
|
|
offset: int = 1 if self._day > [19, 18, 20, 19, 20, 21, 22, 22, 22, 23, 22, 21][m] else 0
|
|
return Constellation.from_index(9 + m + offset)
|
|
|
|
def get_name(self) -> str:
|
|
return self.NAMES[self._day - 1]
|
|
|
|
def __str__(self) -> str:
|
|
return f'{self.get_solar_month()}{self.get_name()}'
|
|
|
|
def next(self, n: int) -> SolarDay:
|
|
return self.get_julian_day().next(n).get_solar_day()
|
|
|
|
def is_before(self, target: SolarDay) -> bool:
|
|
"""
|
|
是否在指定公历日之前
|
|
:param target: 公历日
|
|
:return: true/false
|
|
"""
|
|
y: int = target.get_year()
|
|
if self._year != y:
|
|
return self._year < y
|
|
m: int = target.get_month()
|
|
return self._month < m if self._month != m else self._day < target.get_day()
|
|
|
|
def is_after(self, target: SolarDay) -> bool:
|
|
"""
|
|
是否在指定公历日之后
|
|
:param target: 公历日
|
|
:return: true/false
|
|
"""
|
|
y: int = target.get_year()
|
|
if self._year != y:
|
|
return self._year > y
|
|
m: int = target.get_month()
|
|
return self._month > m if self._month != m else self._day > target.get_day()
|
|
|
|
def get_term(self) -> SolarTerm:
|
|
"""
|
|
:return: 当天所在的节气 SolarTerm
|
|
"""
|
|
return self.get_term_day().get_solar_term()
|
|
|
|
def get_term_day(self) -> SolarTermDay:
|
|
"""
|
|
:return: 节气第几天
|
|
"""
|
|
y: int = self._year
|
|
i: int = self._month * 2
|
|
if i == 24:
|
|
y += 1
|
|
i = 0
|
|
term: SolarTerm = SolarTerm(y, i + 1)
|
|
day: SolarDay = term.get_solar_day()
|
|
while self.is_before(day):
|
|
term = term.next(-1)
|
|
day = term.get_solar_day()
|
|
return SolarTermDay(term, self.subtract(day))
|
|
|
|
def get_solar_week(self, start: int) -> SolarWeek:
|
|
"""
|
|
公历周
|
|
:param start: 起始星期,1234560分别代表星期一至星期天
|
|
:return: 公历周
|
|
"""
|
|
return SolarWeek(self._year, self._month, ceil((self._day + SolarDay(self._year, self._month, 1).get_week().next(-start).get_index()) / 7.0) - 1, start)
|
|
|
|
def get_phenology_day(self) -> PhenologyDay:
|
|
"""
|
|
当天所在的七十二候
|
|
:return: 七十二候 PhenologyDay
|
|
"""
|
|
d: SolarTermDay = self.get_term_day()
|
|
day_index: int = d.get_day_index()
|
|
index: int = day_index // 5
|
|
if index > 2:
|
|
index = 2
|
|
term: SolarTerm = d.get_solar_term()
|
|
return PhenologyDay(Phenology(term.get_year(), term.get_index() * 3 + index), day_index - index * 5)
|
|
|
|
def get_phenology(self) -> Phenology:
|
|
"""
|
|
当天所在的候
|
|
:return: 候 Phenology
|
|
"""
|
|
return self.get_phenology_day().get_phenology()
|
|
|
|
def get_dog_day(self) -> Union[DogDay, None]:
|
|
"""
|
|
当天所在的三伏天
|
|
:return: 三伏天 DogDay
|
|
"""
|
|
# 初伏,夏至后第3个庚日
|
|
d0: SolarDay = Event.builder().term_heaven_stem(12, 6, 20).build().get_solar_day(self._year)
|
|
# 中伏,夏至后第4个庚日
|
|
d1: SolarDay = Event.builder().term_heaven_stem(12, 6, 30).build().get_solar_day(self._year)
|
|
# 末伏,立秋后第1个庚日
|
|
d2: SolarDay = Event.builder().term_heaven_stem(15, 6, 0).build().get_solar_day(self._year)
|
|
if self.is_before(d0) or self.is_after(d2.next(9)):
|
|
return None
|
|
if not self.is_before(d2):
|
|
return DogDay(Dog.from_index(2), self.subtract(d2))
|
|
return DogDay(Dog.from_index(0), self.subtract(d0)) if self.is_before(d1) else DogDay(Dog.from_index(1), self.subtract(d1))
|
|
|
|
def get_plum_rain_day(self) -> Union[PlumRainDay, None]:
|
|
"""
|
|
当天所在的梅雨天(芒种后的第1个丙日入梅,小暑后的第1个未日出梅)
|
|
:return: 梅雨天
|
|
"""
|
|
# 入梅,芒种后第1个丙日
|
|
start: SolarDay = Event.builder().term_heaven_stem(11, 2, 0).build().get_solar_day(self._year)
|
|
# 出梅,小暑后第1个未日
|
|
end: SolarDay = Event.builder().term_earth_branch(13, 7, 0).build().get_solar_day(self._year)
|
|
if self.is_before(start) or self.is_after(end):
|
|
return None
|
|
return PlumRainDay(PlumRain(1), 0) if self == end else PlumRainDay(PlumRain(0), self.subtract(start))
|
|
|
|
def get_hide_heaven_stem_day(self) -> HideHeavenStemDay:
|
|
"""
|
|
:return: 人元司令分野
|
|
"""
|
|
from tyme4py.sixtycycle import HideHeavenStemDay, HideHeavenStem
|
|
day_counts: List[int] = [3, 5, 7, 9, 10, 30]
|
|
term: SolarTerm = self.get_term()
|
|
if term.is_qi():
|
|
term = term.next(-1)
|
|
day_index: int = self.subtract(term.get_solar_day())
|
|
start_index: int = (term.get_index() - 1) * 3
|
|
data: str = '93705542220504xx1513904541632524533533105544806564xx7573304542018584xx95'[start_index: start_index + 6]
|
|
days: int = 0
|
|
heaven_stem_index: int = 0
|
|
type_index: int = 0
|
|
while type_index < 3:
|
|
i: int = type_index * 2
|
|
d: str = data[i: i + 1]
|
|
count: int = 0
|
|
if d != 'x':
|
|
heaven_stem_index = int(d, 10)
|
|
count = day_counts[int(data[i + 1: i + 2], 10)]
|
|
days += count
|
|
|
|
if day_index <= days:
|
|
day_index -= days - count
|
|
break
|
|
|
|
type_index += 1
|
|
|
|
hide_type: HideHeavenStemType
|
|
if type_index == 0:
|
|
hide_type = HideHeavenStemType.RESIDUAL
|
|
elif type_index == 1:
|
|
hide_type = HideHeavenStemType.MIDDLE
|
|
else:
|
|
hide_type = HideHeavenStemType.MAIN
|
|
return HideHeavenStemDay(HideHeavenStem(heaven_stem_index, hide_type), day_index)
|
|
|
|
def get_nine_day(self) -> Union[NineDay, None]:
|
|
"""
|
|
当天所在的数九天
|
|
:return: 数九天 NineDay
|
|
"""
|
|
start: SolarDay = SolarTerm(self._year + 1, 0).get_solar_day()
|
|
if self.is_before(start):
|
|
start = SolarTerm(self._year, 0).get_solar_day()
|
|
|
|
end: SolarDay = start.next(81)
|
|
if self.is_before(start) or (not self.is_before(end)):
|
|
return None
|
|
|
|
days: int = self.subtract(start)
|
|
return NineDay(Nine(int(days / 9)), days % 9)
|
|
|
|
def get_index_in_year(self) -> int:
|
|
"""
|
|
位于当年的索引
|
|
:return: 索引
|
|
"""
|
|
return self.subtract(SolarDay(self._year, 1, 1))
|
|
|
|
def subtract(self, target: SolarDay) -> int:
|
|
"""
|
|
公历日期相减,获得相差天数
|
|
:param target: 公历
|
|
:return: 两个公历日之间相差的天数
|
|
"""
|
|
return int(self.get_julian_day().subtract(target.get_julian_day()))
|
|
|
|
def get_julian_day(self) -> JulianDay:
|
|
"""
|
|
公历日转儒略日
|
|
:return: 儒略日
|
|
"""
|
|
return JulianDay.from_ymd_hms(self._year, self._month, self._day, 0, 0, 0)
|
|
|
|
def get_lunar_day(self) -> LunarDay:
|
|
"""
|
|
:return: 农历日
|
|
"""
|
|
from tyme4py.lunar import LunarMonth, LunarDay
|
|
m: LunarMonth = LunarMonth.from_ym(self._year, self._month)
|
|
days: int = self.subtract(m.get_first_julian_day().get_solar_day())
|
|
while days < 0:
|
|
m = m.next(-1)
|
|
days += m.get_day_count()
|
|
return LunarDay(m.get_year(), m.get_month_with_leap(), days + 1)
|
|
|
|
def get_sixty_cycle_day(self) -> SixtyCycleDay:
|
|
from tyme4py.sixtycycle import SixtyCycleDay
|
|
return SixtyCycleDay.from_solar_day(self)
|
|
|
|
def get_rab_byung_day(self) -> RabByungDay:
|
|
from tyme4py.rabbyung import RabByungDay
|
|
return RabByungDay.from_solar_day(self)
|
|
|
|
def get_legal_holiday(self) -> Union[LegalHoliday, None]:
|
|
"""
|
|
法定假日,如果当天不是法定假日,返回None
|
|
:return: 法定假日
|
|
"""
|
|
return LegalHoliday.from_ymd(self._year, self._month, self._day)
|
|
|
|
def get_festival(self) -> Union[SolarFestival, None]:
|
|
"""
|
|
公历现代节日,如果当天不是公历现代节日,返回None
|
|
:return: 公历现代节日
|
|
"""
|
|
return SolarFestival.from_ymd(self._year, self._month, self._day)
|
|
|
|
def get_phase_day(self) -> PhaseDay:
|
|
"""
|
|
月相第几天
|
|
:return: 月相第几天
|
|
"""
|
|
month = self.get_lunar_day().get_lunar_month().next(1)
|
|
p = Phase.from_index(month.get_year(), month.get_month_with_leap(), 0)
|
|
d = p.get_solar_day()
|
|
while d.is_after(self):
|
|
p = p.next(-1)
|
|
d = p.get_solar_day()
|
|
return PhaseDay(p, self.subtract(d))
|
|
|
|
def get_phase(self) -> Phase:
|
|
"""
|
|
月相
|
|
:return: 月相 Phase
|
|
"""
|
|
return self.get_phase_day().get_phase()
|
|
|
|
def get_nine_star(self) -> NineStar:
|
|
"""
|
|
:return: 九星 NineStar
|
|
"""
|
|
y: int = self.get_year()
|
|
winter_solstice: SolarDay = SolarTerm.from_index(y, 0).get_solar_day()
|
|
summer_solstice: SolarDay = SolarTerm.from_index(y, 12).get_solar_day()
|
|
next_winter_solstice: SolarDay = SolarTerm.from_index(y + 1, 0).get_solar_day()
|
|
w: SolarDay = winter_solstice.next(winter_solstice.get_lunar_day().get_sixty_cycle().steps_close_to(0))
|
|
s: SolarDay = summer_solstice.next(summer_solstice.get_lunar_day().get_sixty_cycle().steps_close_to(0))
|
|
n: SolarDay = next_winter_solstice.next(next_winter_solstice.get_lunar_day().get_sixty_cycle().steps_close_to(0))
|
|
if self.is_before(w):
|
|
return NineStar.from_index(w.subtract(self) - 1)
|
|
if self.is_before(s):
|
|
return NineStar.from_index(self.subtract(w))
|
|
return NineStar.from_index(n.subtract(self) - 1 if self.is_before(n) else self.subtract(n))
|
|
|
|
def get_hijri_day(self) -> HijriDay:
|
|
"""
|
|
:return: 回历 HijriDay
|
|
"""
|
|
d: int = self.subtract(SolarDay(622, 7, 16))
|
|
z = d // 10631
|
|
d -= z * 10631
|
|
y = int(floor((d + 0.5) / 354.366))
|
|
d -= int(floor(y * 354.366 + 0.5))
|
|
m = int(floor((d + 0.11) / 29.51))
|
|
d -= int(floor(m * 29.5 + 0.5))
|
|
from tyme4py.hijri import HijriDay
|
|
return HijriDay(z * 30 + y + 1, m + 1, d + 1)
|
|
|
|
|
|
class SolarTime(SecondUnit):
|
|
|
|
@staticmethod
|
|
def validate(year: int, month: int, day: int, hour: int, minute: int, second: int) -> None:
|
|
SecondUnit.validate(year, month, day, hour, minute, second)
|
|
SolarDay.validate(year, month, day)
|
|
|
|
def __init__(self, year: int, month: int, day: int, hour: int, minute: int, second: int):
|
|
SolarTime.validate(year, month, day, hour, minute, second)
|
|
super().__init__(year, month, day, hour, minute, second)
|
|
|
|
@classmethod
|
|
def from_ymd_hms(cls, year: int, month: int, day: int, hour: int, minute: int, second: int) -> SolarTime:
|
|
"""
|
|
:param year: 年
|
|
:param month: 月
|
|
:param day: 日
|
|
:param hour: 时
|
|
:param minute: 分
|
|
:param second: 秒
|
|
:return: 公历时刻
|
|
"""
|
|
return cls(year, month, day, hour, minute, second)
|
|
|
|
def get_solar_day(self) -> SolarDay:
|
|
"""
|
|
:return: 公历日
|
|
"""
|
|
return SolarDay.from_ymd(self._year, self._month, self._day)
|
|
|
|
def get_name(self) -> str:
|
|
return f'{self._hour:02d}:{self._minute:02d}:{self._second:02d}'
|
|
|
|
def __str__(self) -> str:
|
|
return f'{self.get_solar_day()} {self.get_name()}'
|
|
|
|
def next(self, n: int) -> SolarTime:
|
|
"""
|
|
推移
|
|
:param n: 推移秒数
|
|
:return: 公历时刻
|
|
"""
|
|
if n == 0:
|
|
return SolarTime(self._year, self._month, self._day, self._hour, self._minute, self._second)
|
|
ts: int = self._second + n
|
|
tm: int = self._minute + floor(ts / 60)
|
|
ts %= 60
|
|
if ts < 0:
|
|
ts += 60
|
|
tm -= 1
|
|
th: int = self._hour + int(tm / 60)
|
|
tm %= 60
|
|
if tm < 0:
|
|
tm += 60
|
|
th -= 1
|
|
td: int = int(th / 24)
|
|
th %= 24
|
|
if th < 0:
|
|
th += 24
|
|
td -= 1
|
|
d: SolarDay = self.get_solar_day().next(td)
|
|
return SolarTime(d.get_year(), d.get_month(), d.get_day(), th, tm, ts)
|
|
|
|
def is_before(self, target: SolarTime) -> bool:
|
|
"""
|
|
是否在指定公历时刻之前
|
|
:param target: 公历时刻
|
|
:return: true/false
|
|
"""
|
|
a_day: SolarDay = self.get_solar_day()
|
|
b_day: SolarDay = target.get_solar_day()
|
|
if a_day != b_day:
|
|
return a_day.is_before(b_day)
|
|
h: int = target.get_hour()
|
|
if self._hour != h:
|
|
return self._hour < h
|
|
m: int = target.get_minute()
|
|
return self._minute < m if self._minute != m else self._second < target.get_second()
|
|
|
|
def is_after(self, target: SolarTime) -> bool:
|
|
"""
|
|
是否在指定公历时刻之后
|
|
:param target: 公历时刻
|
|
:return: true/false
|
|
"""
|
|
a_day: SolarDay = self.get_solar_day()
|
|
b_day: SolarDay = target.get_solar_day()
|
|
if a_day != b_day:
|
|
return a_day.is_after(b_day)
|
|
h: int = target.get_hour()
|
|
if self._hour != h:
|
|
return self._hour > h
|
|
m: int = target.get_minute()
|
|
return self._minute > m if self._minute != m else self._second > target.get_second()
|
|
|
|
def get_term(self) -> SolarTerm:
|
|
"""
|
|
当时所在的节气
|
|
:return: 节气 SolarTerm
|
|
"""
|
|
term: SolarTerm = self.get_solar_day().get_term()
|
|
if self.is_before(term.get_julian_day().get_solar_time()):
|
|
term = term.next(-1)
|
|
return term
|
|
|
|
def get_phenology(self) -> Phenology:
|
|
"""
|
|
当时所在的候
|
|
:return: 候 Phenology
|
|
"""
|
|
p: Phenology = self.get_solar_day().get_phenology()
|
|
if self.is_before(p.get_julian_day().get_solar_time()):
|
|
p = p.next(-1)
|
|
return p
|
|
|
|
def get_julian_day(self) -> JulianDay:
|
|
"""
|
|
公历时刻转儒略日
|
|
:return: 儒略日
|
|
"""
|
|
return JulianDay.from_ymd_hms(self._year, self._month, self._day, self._hour, self._minute, self._second)
|
|
|
|
def subtract(self, target: SolarTime) -> int:
|
|
"""
|
|
公历时刻相减,获得相差秒数
|
|
:param target: 公历时刻
|
|
:return: 秒数
|
|
"""
|
|
days: int = self.get_solar_day().subtract(target.get_solar_day())
|
|
cs: int = self._hour * 3600 + self._minute * 60 + self._second
|
|
ts: int = target.get_hour() * 3600 + target.get_minute() * 60 + target.get_second()
|
|
seconds: int = cs - ts
|
|
if seconds < 0:
|
|
seconds += 86400
|
|
days -= 1
|
|
seconds += days * 86400
|
|
return seconds
|
|
|
|
def get_lunar_hour(self) -> LunarHour:
|
|
"""
|
|
农历时辰
|
|
:return: 农历时辰
|
|
"""
|
|
from tyme4py.lunar import LunarDay, LunarHour
|
|
d: LunarDay = self.get_solar_day().get_lunar_day()
|
|
return LunarHour(d.get_year(), d.get_month(), d.get_day(), self._hour, self._minute, self._second)
|
|
|
|
def get_sixty_cycle_hour(self) -> SixtyCycleHour:
|
|
"""
|
|
干支时辰
|
|
:return: 干支时辰
|
|
"""
|
|
from tyme4py.sixtycycle import SixtyCycleHour
|
|
return SixtyCycleHour.from_solar_time(self)
|
|
|
|
def get_phase(self) -> Phase:
|
|
"""
|
|
月相
|
|
:return: 月相 Phase
|
|
"""
|
|
month = self.get_lunar_hour().get_lunar_day().get_lunar_month().next(1)
|
|
p = Phase.from_index(month.get_year(), month.get_month_with_leap(), 0)
|
|
while p.get_solar_time().is_after(self):
|
|
p = p.next(-1)
|
|
return p
|