import copy from typing import TYPE_CHECKING import arrow import regex as re from loguru import logger from .enums import RangeTimeEnum from .helpers.arrow_helper import arrow2grid, grid2arrow from .helpers.lunar_solar_converter import Lunar, LunarSolarConverter from .point import TimePoint from .resource import holiday if TYPE_CHECKING: from .normalizer import TimeNormalizer class TimeUnit: def __init__( self, exp_time: str, normalizer: "TimeNormalizer", contextTp: TimePoint ): """时间语句分析 \n exp_time: 时间表达式 \n normalizer: TimeNormalizer 类 """ # logger.debug("TimeUnit 初始化:") # logger.debug(f" 字段: {exp_time}") # logger.debug(f" 上下文: {contextTp}") self._noyear = False self.exp_time = exp_time self.normalizer = normalizer self.tp = TimePoint() self.tp_origin = contextTp self.isFirstTimeSolveContext = True self.isMorning = False self.isAllDayTime = True self.time = arrow.now("Asia/Shanghai") self.time_normalization() def __repr__(self): if self.normalizer.isTimeDelta: return f"= 0 and self.tp.tunit[tunitpointer] < 0: tunitpointer -= 1 for i in range(0, tunitpointer): if self.tp.tunit[i] < 0: self.tp.tunit[i] = int(time_grid[i]) self.time = self.tp.get_arrow() # logger.debug(f"时间点: {self.time}") def norm_setyear(self): """ 年-规范化方法--该方法识别时间表达式单元的年字段 """ # xx年后 rule = r"([0-9]{1,})(?=年后)" pattern = re.compile(rule) match = pattern.search(self.exp_time) if match is not None: self.tp.set_unit(self.normalizer.baseTime.shift(years=int(match.group()))) return # 一位数表示的年份 rule = r"(? weekday: cur = cur.shift(days=7) return cur def preferFuture(self, checkTimeIndex): """ 如果用户选项是倾向于未来时间,检查checkTimeIndex所指的时间是否是过去的时间,如果是的话,将大一级的时间设为当前时间的+1。 如在晚上说“早上8点看书”,则识别为明天早上; 12月31日说“3号买菜”,则识别为明年1月的3号。 :param checkTimeIndex: _tp.tunit时间数组的下标 """ # 1. 检查被检查的时间级别之前,是否没有更高级的已经确定的时间,如果有,则不进行处理. for i in range(0, checkTimeIndex): if self.tp.tunit[i] != -1: return # 2. 根据上下文补充时间 self.checkContextTime(checkTimeIndex) # 3. 根据上下文补充时间后再次检查被检查的时间级别之前,是否没有更高级的已经确定的时间,如果有,则不进行倾向处理 # TODO 确认是否可以删除掉. # for i in range(0, checkTimeIndex): # if self.tp.tunit[i] != -1: # return # 4. 确认用户选项 if not self.normalizer.isPreferFuture: return # 5. 获取当前时间,如果识别到的时间小于当前时间,则将其上的所有级别时间设置为当前时间,并且其上一级的时间步长+1 time_arr = arrow2grid(self.normalizer.baseTime) cur = self.normalizer.baseTime cur_unit = int(time_arr[checkTimeIndex]) # logger.debug(time_arr) # logger.debug(self.tp.tunit) if self.tp.year == -1: self._noyear = True else: self._noyear = False if cur_unit < self.tp.tunit[checkTimeIndex]: return # if cur_unit == self.tp.tunit[checkTimeIndex]: # down_unit = int(time_arr[checkTimeIndex + 1]) # if down_unit # 准备增加的时间单位是被检查的时间的上一级,将上一级时间+1 cur = self.addTime(cur, checkTimeIndex - 1) time_arr = arrow2grid(cur) for i in range(0, checkTimeIndex): self.tp.tunit[i] = int(time_arr[i]) # if i == 1: # self.tp.tunit[i] += 1 def _check_time(self, parse): """ 检查未来时间点 :param parse: 解析出来的list """ time_arr = arrow2grid(self.normalizer.baseTime) if self._noyear: # check the month # logger.debug(parse) # logger.debug(time_arr) if parse[1] == int(time_arr[1]): if parse[2] > int(time_arr[2]): parse[0] = parse[0] - 1 self._noyear = False def checkContextTime(self, checkTimeIndex: int): """ 根据上下文时间补充时间信息 :param checkTimeIndex: """ if not self.isFirstTimeSolveContext: return for i in range(0, checkTimeIndex): if self.tp.tunit[i] == -1 and self.tp_origin.tunit[i] != -1: self.tp.tunit[i] = self.tp_origin.tunit[i] # 在处理小时这个级别时,如果上文时间是下午的且下文没有主动声明小时级别以上的时间,则也把下文时间设为下午 if ( self.isFirstTimeSolveContext is True and checkTimeIndex == 3 and self.tp_origin.tunit[checkTimeIndex] >= 12 > self.tp.tunit[checkTimeIndex] ): self.tp.tunit[checkTimeIndex] += 12 self.isFirstTimeSolveContext = False def addTime(self, cur: arrow.Arrow, fore_unit: int) -> arrow.Arrow: if fore_unit == 0: cur = cur.shift(years=1) elif fore_unit == 1: cur = cur.shift(months=1) elif fore_unit == 2: cur = cur.shift(days=1) elif fore_unit == 3: cur = cur.shift(hours=1) elif fore_unit == 4: cur = cur.shift(minutes=1) elif fore_unit == 5: cur = cur.shift(seconds=1) return cur