migration: establish exact preserved app baseline
This commit is contained in:
Vendored
+465
@@ -0,0 +1,465 @@
|
||||
# -*- 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())
|
||||
Reference in New Issue
Block a user