unit.py 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102
  1. import copy
  2. from typing import TYPE_CHECKING
  3. import arrow
  4. import regex as re
  5. from loguru import logger
  6. from .enums import RangeTimeEnum
  7. from .helpers.arrow_helper import arrow2grid, grid2arrow
  8. from .helpers.lunar_solar_converter import Lunar, LunarSolarConverter
  9. from .point import TimePoint
  10. from .resource import holiday
  11. if TYPE_CHECKING:
  12. from .normalizer import TimeNormalizer
  13. class TimeUnit:
  14. def __init__(
  15. self, exp_time: str, normalizer: "TimeNormalizer", contextTp: TimePoint
  16. ):
  17. """时间语句分析 \n
  18. exp_time: 时间表达式 \n
  19. normalizer: TimeNormalizer 类
  20. """
  21. # logger.debug("TimeUnit 初始化:")
  22. # logger.debug(f" 字段: {exp_time}")
  23. # logger.debug(f" 上下文: {contextTp}")
  24. self._noyear = False
  25. self.exp_time = exp_time
  26. self.normalizer = normalizer
  27. self.tp = TimePoint()
  28. self.tp_origin = contextTp
  29. self.isFirstTimeSolveContext = True
  30. self.isMorning = False
  31. self.isAllDayTime = True
  32. self.time = arrow.now("Asia/Shanghai")
  33. self.time_normalization()
  34. def __repr__(self):
  35. if self.normalizer.isTimeDelta:
  36. return f"<TimeUnit(Delta) {self.normalizer.timeDelta})"
  37. else:
  38. return f"<TimeUnit {self.time}"
  39. def time_normalization(self):
  40. self.norm_setyear()
  41. self.norm_setmonth()
  42. self.norm_setday()
  43. self.norm_setmonth_fuzzyday()
  44. # self.norm_setBaseRelated()
  45. self.norm_setCurRelated()
  46. self.norm_sethour()
  47. self.norm_setminute()
  48. self.norm_setsecond()
  49. self.norm_setSpecial()
  50. self.norm_setSpanRelated()
  51. self.norm_setHoliday()
  52. self.modifyTimeBase()
  53. self.tp_origin.tunit = copy.deepcopy(self.tp.tunit)
  54. # logger.debug(f"self.tp: {self.tp}")
  55. # 判断是时间点还是时间区间
  56. spanFlag = True
  57. for i in range(0, 4):
  58. if self.tp.tunit[i] != -1:
  59. spanFlag = False
  60. if spanFlag:
  61. self.normalizer.isTimeDelta = True
  62. # if self.normalizer.isTimeDelta:
  63. # logger.debug("isTimeDelta")
  64. # else:
  65. # logger.debug("判断是时间点")
  66. if self.normalizer.isTimeDelta:
  67. if not self.tp.is_valid():
  68. # logger.debug("self.tp is invalid.")
  69. return
  70. self.normalizer.timeDelta = self.tp.gen_delta()
  71. # logger.debug(f"时间间隔: {self.normalizer.timeDelta}")
  72. return
  73. time_grid = arrow2grid(self.normalizer.baseTime)
  74. tunitpointer = 5
  75. while tunitpointer >= 0 and self.tp.tunit[tunitpointer] < 0:
  76. tunitpointer -= 1
  77. for i in range(0, tunitpointer):
  78. if self.tp.tunit[i] < 0:
  79. self.tp.tunit[i] = int(time_grid[i])
  80. self.time = self.tp.get_arrow()
  81. # logger.debug(f"时间点: {self.time}")
  82. def norm_setyear(self):
  83. """
  84. 年-规范化方法--该方法识别时间表达式单元的年字段
  85. """
  86. # xx年后
  87. rule = r"([0-9]{1,})(?=年后)"
  88. pattern = re.compile(rule)
  89. match = pattern.search(self.exp_time)
  90. if match is not None:
  91. self.tp.set_unit(self.normalizer.baseTime.shift(years=int(match.group())))
  92. return
  93. # 一位数表示的年份
  94. rule = r"(?<![0-9])[0-9]{1}(?=年)"
  95. pattern = re.compile(rule)
  96. match = pattern.search(self.exp_time)
  97. if match is not None:
  98. self.normalizer.isTimeDelta = True
  99. year = int(match.group())
  100. self.tp.year = year
  101. # 两位数表示的年份
  102. rule = r"[0-9]{2}(?=年)"
  103. pattern = re.compile(rule)
  104. match = pattern.search(self.exp_time)
  105. if match is not None:
  106. year = int(match.group())
  107. self.tp.year = year
  108. # 三位数表示的年份
  109. rule = r"(?<![0-9])[0-9]{3}(?=年)"
  110. pattern = re.compile(rule)
  111. match = pattern.search(self.exp_time)
  112. if match is not None:
  113. self.normalizer.isTimeDelta = True
  114. year = int(match.group())
  115. self.tp.year = year
  116. # 四位数表示的年份
  117. rule = r"[0-9]{4}(?=年)"
  118. pattern = re.compile(rule)
  119. match = pattern.search(self.exp_time)
  120. if match is not None:
  121. year = int(match.group())
  122. self.tp.year = year
  123. def norm_setmonth(self):
  124. """
  125. 月-规范化方法--该方法识别时间表达式单元的月字段
  126. """
  127. rule = r"((10)|(11)|(12)|([1-9]))(?=月)"
  128. pattern = re.compile(rule)
  129. match = pattern.search(self.exp_time)
  130. if match is not None:
  131. self.tp.month = int(match.group())
  132. # 处理倾向于未来时间的情况
  133. self.preferFuture(1)
  134. def norm_setmonth_fuzzyday(self):
  135. """
  136. 月-日 兼容模糊写法:该方法识别时间表达式单元的月、日字段
  137. """
  138. rule = r"((10)|(11)|(12)|([1-9]))(月|\.|\-)([0-3][0-9]|[1-9])"
  139. pattern = re.compile(rule)
  140. match = pattern.search(self.exp_time)
  141. if match is not None:
  142. matchStr = match.group()
  143. p = re.compile(r"(月|\.|\-)")
  144. m = p.search(matchStr)
  145. if m is not None:
  146. splitIndex = m.start()
  147. month = matchStr[0:splitIndex]
  148. day = matchStr[splitIndex + 1:]
  149. self.tp.month = int(month)
  150. self.tp.day = int(day)
  151. # 处理倾向于未来时间的情况
  152. self.preferFuture(1)
  153. self._check_time(self.tp.tunit)
  154. def norm_setday(self):
  155. """
  156. 日-规范化方法:该方法识别时间表达式单元的日字段
  157. """
  158. rule = r"((?<!\d))([0-3][0-9]|[1-9])(?=(日|号))"
  159. pattern = re.compile(rule)
  160. match = pattern.search(self.exp_time)
  161. if match is not None:
  162. self.tp.day = int(match.group())
  163. # 处理倾向于未来时间的情况
  164. self.preferFuture(2)
  165. self._check_time(self.tp.tunit)
  166. rule = r"((?<!\d))([0-3][0-9]|[1-9])(?=(日|天)后)"
  167. pattern = re.compile(rule)
  168. match = pattern.search(self.exp_time)
  169. if match is not None:
  170. self.normalizer.isTimeDelta = True
  171. self.tp.day = int(match.group())
  172. self._check_time(self.tp.tunit)
  173. def norm_checkKeyword(self):
  174. # * 对关键字:早(包含早上/早晨/早间),上午,中午,午间,下午,午后,晚上,傍晚,晚间,晚,pm,PM的正确时间计算
  175. # * 规约:
  176. # * 1.中午/午间0-10点视为12-22点
  177. # * 2.下午/午后0-11点视为12-23点
  178. # * 3.晚上/傍晚/晚间/晚1-11点视为13-23点,12点视为0点
  179. # * 4.0-11点pm/PM视为12-23点
  180. rule = r"凌晨"
  181. pattern = re.compile(rule)
  182. match = pattern.search(self.exp_time)
  183. if match is not None:
  184. if self.tp.hour == -1: # 增加对没有明确时间点,只写了“凌晨”这种情况的处理
  185. self.tp.hour = RangeTimeEnum.day_break
  186. elif 12 <= self.tp.hour <= 23:
  187. self.tp.hour -= 12
  188. elif self.tp.hour == 0:
  189. self.tp.hour = 12
  190. # 处理倾向于未来时间的情况
  191. self.preferFuture(3)
  192. self.isAllDayTime = False
  193. rule = r"早上|早晨|早间|晨间|今早|明早|早|清晨"
  194. pattern = re.compile(rule)
  195. match = pattern.search(self.exp_time)
  196. if match is not None:
  197. if self.tp.hour == -1: # 增加对没有明确时间点,只写了“早上/早晨/早间”这种情况的处理
  198. self.tp.hour = RangeTimeEnum.early_morning
  199. # 处理倾向于未来时间的情况
  200. elif 12 <= self.tp.hour <= 23:
  201. self.tp.hour -= 12
  202. elif self.tp.hour == 0:
  203. self.tp.hour = 12
  204. self.preferFuture(3)
  205. self.isAllDayTime = False
  206. rule = r"上午"
  207. pattern = re.compile(rule)
  208. match = pattern.search(self.exp_time)
  209. if match is not None:
  210. if self.tp.hour == -1: # 增加对没有明确时间点,只写了“上午”这种情况的处理
  211. self.tp.hour = RangeTimeEnum.morning
  212. elif 12 <= self.tp.hour <= 23:
  213. self.tp.hour -= 12
  214. elif self.tp.hour == 0:
  215. self.tp.hour = 12
  216. # 处理倾向于未来时间的情况
  217. self.preferFuture(3)
  218. self.isAllDayTime = False
  219. rule = r"(中午)|(午间)|(白天)"
  220. pattern = re.compile(rule)
  221. match = pattern.search(self.exp_time)
  222. if match is not None:
  223. if 0 <= self.tp.hour <= 10:
  224. self.tp.hour += 12
  225. if self.tp.hour == -1: # 增加对没有明确时间点,只写了“中午/午间”这种情况的处理
  226. self.tp.hour = RangeTimeEnum.noon
  227. # 处理倾向于未来时间的情况
  228. self.preferFuture(3)
  229. self.isAllDayTime = False
  230. rule = r"(下午)|(午后)|(pm)|(PM)"
  231. pattern = re.compile(rule)
  232. match = pattern.search(self.exp_time)
  233. if match is not None:
  234. if 0 <= self.tp.hour <= 11:
  235. self.tp.hour += 12
  236. if self.tp.hour == -1: # 增加对没有明确时间点,只写了“下午|午后”这种情况的处理
  237. self.tp.hour = RangeTimeEnum.afternoon
  238. # 处理倾向于未来时间的情况
  239. self.preferFuture(3)
  240. self.isAllDayTime = False
  241. rule = r"晚上|夜间|夜里|今晚|明晚|晚|夜里"
  242. pattern = re.compile(rule)
  243. match = pattern.search(self.exp_time)
  244. if match is not None:
  245. if 0 <= self.tp.hour <= 11:
  246. self.tp.hour += 12
  247. elif self.tp.hour == 12:
  248. self.tp.hour = 0
  249. elif self.tp.hour == -1: # 增加对没有明确时间点,只写了“下午|午后”这种情况的处理
  250. self.tp.hour = RangeTimeEnum.lateNight
  251. # 处理倾向于未来时间的情况
  252. self.preferFuture(3)
  253. self.isAllDayTime = False
  254. def norm_sethour(self):
  255. """
  256. 时-规范化方法:该方法识别时间表达式单元的时字段
  257. """
  258. rule = r"(?<!(周|星期))([0-2]?[0-9])(?=(点|时))"
  259. pattern = re.compile(rule)
  260. match = pattern.search(self.exp_time)
  261. if match is not None:
  262. self.tp.hour = int(match.group())
  263. self.norm_checkKeyword()
  264. # 处理倾向于未来时间的情况
  265. self.preferFuture(3)
  266. self.isAllDayTime = False
  267. else:
  268. self.norm_checkKeyword()
  269. def norm_setminute(self):
  270. """
  271. 分-规范化方法:该方法识别时间表达式单元的分字段
  272. """
  273. rule = r"([0-9]+(?=分(?!钟)))|((?<=((?<!小)[点时]))[0-5]?[0-9](?!刻))"
  274. pattern = re.compile(rule)
  275. match = pattern.search(self.exp_time)
  276. if match is not None:
  277. if match.group() != "":
  278. self.tp.minute = int(match.group())
  279. # 处理倾向于未来时间的情况
  280. # self.preferFuture(4)
  281. self.isAllDayTime = False
  282. # 加对一刻,半,3刻的正确识别(1刻为15分,半为30分,3刻为45分)
  283. rule = r"(?<=[点时])[1一]刻(?!钟)"
  284. pattern = re.compile(rule)
  285. match = pattern.search(self.exp_time)
  286. if match is not None:
  287. self.tp.minute = 15
  288. # 处理倾向于未来时间的情况
  289. # self.preferFuture(4)
  290. self.isAllDayTime = False
  291. rule = r"(?<=[点时])半"
  292. pattern = re.compile(rule)
  293. match = pattern.search(self.exp_time)
  294. if match is not None:
  295. self.tp.minute = 30
  296. # 处理倾向于未来时间的情况
  297. self.preferFuture(4)
  298. self.isAllDayTime = False
  299. rule = r"(?<=[点时])[3三]刻(?!钟)"
  300. pattern = re.compile(rule)
  301. match = pattern.search(self.exp_time)
  302. if match is not None:
  303. self.tp.minute = 45
  304. # 处理倾向于未来时间的情况
  305. # self.preferFuture(4)
  306. self.isAllDayTime = False
  307. def norm_setsecond(self):
  308. """
  309. 添加了省略“秒”说法的时间:如17点15分32
  310. """
  311. rule = r"([0-9]+(?=秒))|((?<=分)[0-5]?[0-9])"
  312. pattern = re.compile(rule)
  313. match = pattern.search(self.exp_time)
  314. if match is not None:
  315. self.tp.second = int(match.group())
  316. self.isAllDayTime = False
  317. def norm_setSpecial(self):
  318. """
  319. 特殊形式的规范化方法-该方法识别特殊形式的时间表达式单元的各个字段
  320. """
  321. rule = r"(晚上|夜间|夜里|今晚|明晚|晚|夜里|下午|午后)(?<!(周|星期))([0-2]?[0-9]):[0-5]?[0-9]:[0-5]?[0-9]"
  322. pattern = re.compile(rule)
  323. match = pattern.search(self.exp_time)
  324. if match is not None:
  325. rule = r"([0-2]?[0-9]):[0-5]?[0-9]:[0-5]?[0-9]"
  326. pattern = re.compile(rule)
  327. match = pattern.search(self.exp_time)
  328. tmp_target = match.group()
  329. tmp_parser = tmp_target.split(":")
  330. if 0 <= int(tmp_parser[0]) <= 11:
  331. self.tp.hour = int(tmp_parser[0]) + 12
  332. else:
  333. self.tp.hour = int(tmp_parser[0])
  334. self.tp.minute = int(tmp_parser[1])
  335. self.tp.second = int(tmp_parser[2])
  336. # 处理倾向于未来时间的情况
  337. self.preferFuture(3)
  338. self.isAllDayTime = False
  339. else:
  340. rule = r"(晚上|夜间|夜里|今晚|明晚|晚|夜里|下午|午后)(?<!(周|星期))([0-2]?[0-9]):[0-5]?[0-9]"
  341. pattern = re.compile(rule)
  342. match = pattern.search(self.exp_time)
  343. if match is not None:
  344. rule = r"([0-2]?[0-9]):[0-5]?[0-9]"
  345. pattern = re.compile(rule)
  346. match = pattern.search(self.exp_time)
  347. tmp_target = match.group()
  348. tmp_parser = tmp_target.split(":")
  349. if 0 <= int(tmp_parser[0]) <= 11:
  350. self.tp.hour = int(tmp_parser[0]) + 12
  351. else:
  352. self.tp.hour = int(tmp_parser[0])
  353. self.tp.minute = int(tmp_parser[1])
  354. # 处理倾向于未来时间的情况
  355. self.preferFuture(3)
  356. self.isAllDayTime = False
  357. if match is None:
  358. rule = r"(?<!(周|星期))([0-2]?[0-9]):[0-5]?[0-9]:[0-5]?[0-9](PM|pm|p\.m)"
  359. pattern = re.compile(rule, re.I)
  360. match = pattern.search(self.exp_time)
  361. if match is not None:
  362. rule = r"([0-2]?[0-9]):[0-5]?[0-9]:[0-5]?[0-9]"
  363. pattern = re.compile(rule)
  364. match = pattern.search(self.exp_time)
  365. tmp_target = match.group()
  366. tmp_parser = tmp_target.split(":")
  367. if 0 <= int(tmp_parser[0]) <= 11:
  368. self.tp.hour = int(tmp_parser[0]) + 12
  369. else:
  370. self.tp.hour = int(tmp_parser[0])
  371. self.tp.minute = int(tmp_parser[1])
  372. self.tp.second = int(tmp_parser[2])
  373. # 处理倾向于未来时间的情况
  374. self.preferFuture(3)
  375. self.isAllDayTime = False
  376. else:
  377. rule = r"(?<!(周|星期))([0-2]?[0-9]):[0-5]?[0-9](PM|pm|p.m)"
  378. pattern = re.compile(rule, re.I)
  379. match = pattern.search(self.exp_time)
  380. if match is not None:
  381. rule = r"([0-2]?[0-9]):[0-5]?[0-9]"
  382. pattern = re.compile(rule)
  383. match = pattern.search(self.exp_time)
  384. tmp_target = match.group()
  385. tmp_parser = tmp_target.split(":")
  386. if 0 <= int(tmp_parser[0]) <= 11:
  387. self.tp.hour = int(tmp_parser[0]) + 12
  388. else:
  389. self.tp.hour = int(tmp_parser[0])
  390. self.tp.minute = int(tmp_parser[1])
  391. # 处理倾向于未来时间的情况
  392. self.preferFuture(3)
  393. self.isAllDayTime = False
  394. if match is None:
  395. rule = r"(?<!(周|星期|晚上|夜间|夜里|今晚|明晚|晚|夜里|下午|午后))([0-2]?[0-9]):[0-5]?[0-9]:[0-5]?[0-9]"
  396. pattern = re.compile(rule)
  397. match = pattern.search(self.exp_time)
  398. if match is not None:
  399. tmp_target = match.group()
  400. tmp_parser = tmp_target.split(":")
  401. self.tp.hour = int(tmp_parser[0])
  402. self.tp.minute = int(tmp_parser[1])
  403. self.tp.second = int(tmp_parser[2])
  404. # 处理倾向于未来时间的情况
  405. self.preferFuture(3)
  406. self.isAllDayTime = False
  407. else:
  408. rule = r"(?<!(周|星期|晚上|夜间|夜里|今晚|明晚|晚|夜里|下午|午后))([0-2]?[0-9]):[0-5]?[0-9]"
  409. pattern = re.compile(rule)
  410. match = pattern.search(self.exp_time)
  411. if match is not None:
  412. tmp_target = match.group()
  413. tmp_parser = tmp_target.split(":")
  414. self.tp.hour = int(tmp_parser[0])
  415. self.tp.minute = int(tmp_parser[1])
  416. # 处理倾向于未来时间的情况
  417. self.preferFuture(3)
  418. self.isAllDayTime = False
  419. # 这里是对年份表达的极好方式
  420. rule = (
  421. r"[0-9]?[0-9]?[0-9]{2}-((10)|(11)|(12)|([1-9]))-((?<!\d))([0-3][0-9]|[1-9])"
  422. )
  423. pattern = re.compile(rule)
  424. match = pattern.search(self.exp_time)
  425. if match is not None:
  426. tmp_target = match.group()
  427. tmp_parser = tmp_target.split("-")
  428. self.tp.year = int(tmp_parser[0])
  429. self.tp.month = int(tmp_parser[1])
  430. self.tp.day = int(tmp_parser[2])
  431. rule = (
  432. r"[0-9]?[0-9]?[0-9]{2}/((10)|(11)|(12)|([1-9]))/((?<!\d))([0-3][0-9]|[1-9])"
  433. )
  434. pattern = re.compile(rule)
  435. match = pattern.search(self.exp_time)
  436. if match is not None:
  437. tmp_target = match.group()
  438. tmp_parser = tmp_target.split("/")
  439. self.tp.year = int(tmp_parser[0])
  440. self.tp.month = int(tmp_parser[1])
  441. self.tp.day = int(tmp_parser[2])
  442. rule = (
  443. r"((10)|(11)|(12)|([1-9]))/((?<!\d))([0-3][0-9]|[1-9])/[0-9]?[0-9]?[0-9]{2}"
  444. )
  445. pattern = re.compile(rule)
  446. match = pattern.search(self.exp_time)
  447. if match is not None:
  448. tmp_target = match.group()
  449. tmp_parser = tmp_target.split("/")
  450. self.tp.month = int(tmp_parser[0])
  451. self.tp.day = int(tmp_parser[1])
  452. self.tp.year = int(tmp_parser[2])
  453. rule = r"[0-9]?[0-9]?[0-9]{2}\.((10)|(11)|(12)|([1-9]))\.((?<!\d))([0-3][0-9]|[1-9])"
  454. pattern = re.compile(rule)
  455. match = pattern.search(self.exp_time)
  456. if match is not None:
  457. tmp_target = match.group()
  458. tmp_parser = tmp_target.split(".")
  459. self.tp.year = int(tmp_parser[0])
  460. self.tp.month = int(tmp_parser[1])
  461. self.tp.day = int(tmp_parser[2])
  462. def norm_setBaseRelated(self):
  463. """
  464. 设置以上文时间为基准的时间偏移计算
  465. """
  466. # logger.debug(f"设置以上文时间为基准的时间偏移计算: {self.exp_time}")
  467. cur = self.normalizer.baseTime
  468. flag = [False, False, False]
  469. rule = r"\d+(?=天[以之]?前)"
  470. pattern = re.compile(rule)
  471. match = pattern.search(self.exp_time)
  472. if match is not None:
  473. flag[2] = True
  474. day = int(match.group())
  475. cur = cur.shift(days=-day)
  476. rule = r"\d+(?=天[以之]?后)"
  477. pattern = re.compile(rule)
  478. match = pattern.search(self.exp_time)
  479. if match is not None:
  480. flag[2] = True
  481. day = int(match.group())
  482. cur = cur.shift(days=day)
  483. rule = r"\d+(?=(个)?月[以之]?前)"
  484. pattern = re.compile(rule)
  485. match = pattern.search(self.exp_time)
  486. if match is not None:
  487. flag[1] = True
  488. month = int(match.group())
  489. cur = cur.shift(months=-month)
  490. rule = r"\d+(?=(个)?月[以之]?后)"
  491. pattern = re.compile(rule)
  492. match = pattern.search(self.exp_time)
  493. if match is not None:
  494. flag[1] = True
  495. month = int(match.group())
  496. cur = cur.shift(months=month)
  497. rule = r"\d+(?=年[以之]?前)"
  498. pattern = re.compile(rule)
  499. match = pattern.search(self.exp_time)
  500. if match is not None:
  501. flag[0] = True
  502. year = int(match.group())
  503. cur = cur.shift(years=-year)
  504. rule = r"\d+(?=年[以之]?后)"
  505. pattern = re.compile(rule)
  506. match = pattern.search(self.exp_time)
  507. if match is not None:
  508. flag[0] = True
  509. year = int(match.group())
  510. cur = cur.shift(years=year)
  511. if flag[0] or flag[1] or flag[2]:
  512. self.tp.year = int(cur.year)
  513. if flag[1] or flag[2]:
  514. self.tp.month = int(cur.month)
  515. if flag[2]:
  516. self.tp.day = int(cur.day)
  517. # todo 时间长度相关
  518. def norm_setSpanRelated(self):
  519. """
  520. 设置时间长度相关的时间表达式
  521. """
  522. rule = r"\d+(?=个月(?![以之]?[前后]))"
  523. pattern = re.compile(rule)
  524. match = pattern.search(self.exp_time)
  525. if match is not None:
  526. self.normalizer.isTimeDelta = True
  527. month = int(match.group())
  528. self.tp.month = int(month)
  529. rule = r"\d+(?=天(?![以之]?[前后]))"
  530. pattern = re.compile(rule)
  531. match = pattern.search(self.exp_time)
  532. if match is not None:
  533. self.normalizer.isTimeDelta = True
  534. day = int(match.group())
  535. self.tp.day = int(day)
  536. rule = r"\d+(?=(个)?小时(?![以之]?[前后]))"
  537. pattern = re.compile(rule)
  538. match = pattern.search(self.exp_time)
  539. if match is not None:
  540. self.normalizer.isTimeDelta = True
  541. hour = int(match.group())
  542. self.tp.hour = int(hour)
  543. rule = r"\d+(?=分钟(?![以之]?[前后]))"
  544. pattern = re.compile(rule)
  545. match = pattern.search(self.exp_time)
  546. if match is not None:
  547. self.normalizer.isTimeDelta = True
  548. minute = int(match.group())
  549. self.tp.minute = int(minute)
  550. rule = r"\d+(?=秒钟(?![以之]?[前后]))"
  551. pattern = re.compile(rule)
  552. match = pattern.search(self.exp_time)
  553. if match is not None:
  554. self.normalizer.isTimeDelta = True
  555. second = int(match.group())
  556. self.tp.second = int(second)
  557. rule = r"\d+(?=(个)?(周|星期|礼拜)(?![以之]?[前后]))"
  558. pattern = re.compile(rule)
  559. match = pattern.search(self.exp_time)
  560. if match is not None:
  561. self.normalizer.isTimeDelta = True
  562. week = int(match.group())
  563. if self.tp.day == -1:
  564. self.tp.day = 0
  565. self.tp.day += int(week * 7)
  566. # 节假日相关
  567. def norm_setHoliday(self):
  568. rule = (
  569. r"(情人节)|(母亲节)|(青年节)|(教师节)|(中元节)|(端午)|(劳动节)|(7夕)|(建党节)|(建军节)|(初13)|(初14)|(初15)|"
  570. r"(初12)|(初11)|(初9)|(初8)|(初7)|(初6)|(初5)|(初4)|(初3)|(初2)|(初1)|(中和节)|(圣诞)|(中秋)|(春节)|(元宵)|"
  571. r"(航海日)|(儿童节)|(国庆)|(植树节)|(元旦)|(重阳节)|(妇女节)|(记者节)|(立春)|(雨水)|(惊蛰)|(春分)|(清明)|(谷雨)|"
  572. r"(立夏)|(小满 )|(芒种)|(夏至)|(小暑)|(大暑)|(立秋)|(处暑)|(白露)|(秋分)|(寒露)|(霜降)|(立冬)|(小雪)|(大雪)|"
  573. r"(冬至)|(小寒)|(大寒)"
  574. )
  575. pattern = re.compile(rule)
  576. match = pattern.search(self.exp_time)
  577. if match is not None:
  578. month = 0
  579. day = 0
  580. if self.tp.year == -1:
  581. year = self.normalizer.baseTime.year
  582. self.tp.year = int(year)
  583. holi = match.group()
  584. if "节" not in holi:
  585. holi += "节"
  586. if holi in holiday.solar:
  587. month, day = holiday.solar[holi].split("-")
  588. elif holi in holiday.lunar:
  589. date = holiday.lunar[holi].split("-")
  590. lsConverter = LunarSolarConverter()
  591. lunar = Lunar(self.tp.year, int(date[0]), int(date[1]), False)
  592. solar = lsConverter.LunarToSolar(lunar)
  593. self.tp.year = solar.solarYear
  594. month = solar.solarMonth
  595. day = solar.solarDay
  596. else:
  597. holi = holi.strip("节")
  598. if holi in ["小寒", "大寒"]:
  599. self.tp.year += 1
  600. month, day = self.china_24_st(self.tp.year, holi)
  601. self.tp.month = int(month)
  602. self.tp.day = int(day)
  603. def china_24_st(self, year: int, china_st: str):
  604. """
  605. 二十世纪和二十一世纪,24节气计算
  606. :param year: 年份
  607. :param china_st: 节气
  608. :return: 节气日期(月, 日)
  609. """
  610. if (19 == year // 100) or (2000 == year):
  611. # 20世纪 key值
  612. st_key = [
  613. 6.11,
  614. 20.84,
  615. 4.6295,
  616. 19.4599,
  617. 6.3826,
  618. 21.4155,
  619. 5.59,
  620. 20.888,
  621. 6.318,
  622. 21.86,
  623. 6.5,
  624. 22.2,
  625. 7.928,
  626. 23.65,
  627. 8.35,
  628. 23.95,
  629. 8.44,
  630. 23.822,
  631. 9.098,
  632. 24.218,
  633. 8.218,
  634. 23.08,
  635. 7.9,
  636. 22.6,
  637. ]
  638. else:
  639. # 21世纪 key值
  640. st_key = [
  641. 5.4055,
  642. 20.12,
  643. 3.87,
  644. 18.73,
  645. 5.63,
  646. 20.646,
  647. 4.81,
  648. 20.1,
  649. 5.52,
  650. 21.04,
  651. 5.678,
  652. 21.37,
  653. 7.108,
  654. 22.83,
  655. 7.5,
  656. 23.13,
  657. 7.646,
  658. 23.042,
  659. 8.318,
  660. 23.438,
  661. 7.438,
  662. 22.36,
  663. 7.18,
  664. 21.94,
  665. ]
  666. # 二十四节气字典-- key值, 月份,(特殊年份,相差天数)...
  667. solar_terms = {
  668. "小寒": [st_key[0], "1", (2019, -1), (1982, 1)],
  669. "大寒": [st_key[1], "1", (2082, 1)],
  670. "立春": [st_key[2], "2", (None, 0)],
  671. "雨水": [st_key[3], "2", (2026, -1)],
  672. "惊蛰": [st_key[4], "3", (None, 0)],
  673. "春分": [st_key[5], "3", (2084, 1)],
  674. "清明": [st_key[6], "4", (None, 0)],
  675. "谷雨": [st_key[7], "4", (None, 0)],
  676. "立夏": [st_key[8], "5", (1911, 1)],
  677. "小满": [st_key[9], "5", (2008, 1)],
  678. "芒种": [st_key[10], "6", (1902, 1)],
  679. "夏至": [st_key[11], "6", (None, 0)],
  680. "小暑": [st_key[12], "7", (2016, 1), (1925, 1)],
  681. "大暑": [st_key[13], "7", (1922, 1)],
  682. "立秋": [st_key[14], "8", (2002, 1)],
  683. "处暑": [st_key[15], "8", (None, 0)],
  684. "白露": [st_key[16], "9", (1927, 1)],
  685. "秋分": [st_key[17], "9", (None, 0)],
  686. "寒露": [st_key[18], "10", (2088, 0)],
  687. "霜降": [st_key[19], "10", (2089, 1)],
  688. "立冬": [st_key[20], "11", (2089, 1)],
  689. "小雪": [st_key[21], "11", (1978, 0)],
  690. "大雪": [st_key[22], "12", (1954, 1)],
  691. "冬至": [st_key[23], "12", (2021, -1), (1918, -1)],
  692. }
  693. if china_st in ["小寒", "大寒", "立春", "雨水"]:
  694. flag_day = int((year % 100) * 0.2422 + solar_terms[china_st][0]) - int(
  695. (year % 100 - 1) / 4
  696. )
  697. else:
  698. flag_day = int((year % 100) * 0.2422 + solar_terms[china_st][0]) - int(
  699. (year % 100) / 4
  700. )
  701. # 特殊年份处理
  702. for special in solar_terms[china_st][2:]:
  703. if year == special[0]:
  704. flag_day += special[1]
  705. break
  706. return (solar_terms[china_st][1]), str(flag_day)
  707. def norm_setCurRelated(self):
  708. """
  709. 设置当前时间相关的时间表达式
  710. """
  711. # 这一块还是用了断言表达式
  712. cur = self.normalizer.baseTime
  713. flag = [False, False, False]
  714. rule = r"前年"
  715. pattern = re.compile(rule)
  716. match = pattern.search(self.exp_time)
  717. if match is not None:
  718. flag[0] = True
  719. cur = cur.shift(years=-2)
  720. rule = r"去年"
  721. pattern = re.compile(rule)
  722. match = pattern.search(self.exp_time)
  723. if match is not None:
  724. flag[0] = True
  725. cur = cur.shift(years=-1)
  726. rule = r"今年"
  727. pattern = re.compile(rule)
  728. match = pattern.search(self.exp_time)
  729. if match is not None:
  730. flag[0] = True
  731. cur = cur.shift(years=0)
  732. rule = r"明年"
  733. pattern = re.compile(rule)
  734. match = pattern.search(self.exp_time)
  735. if match is not None:
  736. flag[0] = True
  737. cur = cur.shift(years=1)
  738. rule = r"后年"
  739. pattern = re.compile(rule)
  740. match = pattern.search(self.exp_time)
  741. if match is not None:
  742. flag[0] = True
  743. cur = cur.shift(years=2)
  744. rule = r"上*上(个)?月"
  745. pattern = re.compile(rule)
  746. match = pattern.search(self.exp_time)
  747. if match is not None:
  748. flag[1] = True
  749. rule = "上"
  750. pattern = re.compile(rule)
  751. match = pattern.findall(self.exp_time)
  752. cur = cur.shift(months=-len(match))
  753. rule = r"(本|这个)月"
  754. pattern = re.compile(rule)
  755. match = pattern.search(self.exp_time)
  756. if match is not None:
  757. flag[1] = True
  758. cur = cur.shift(months=0)
  759. rule = r"下*下(个)?月"
  760. pattern = re.compile(rule)
  761. match = pattern.search(self.exp_time)
  762. if match is not None:
  763. flag[1] = True
  764. rule = "下"
  765. pattern = re.compile(rule)
  766. match = pattern.findall(self.exp_time)
  767. cur = cur.shift(months=len(match))
  768. rule = r"大*大前天"
  769. pattern = re.compile(rule)
  770. match = pattern.search(self.exp_time)
  771. if match is not None:
  772. flag[2] = True
  773. rule = "大"
  774. pattern = re.compile(rule)
  775. match = pattern.findall(self.exp_time)
  776. cur = cur.shift(days=-(2 + len(match)))
  777. rule = r"(?<!大)前天"
  778. pattern = re.compile(rule)
  779. match = pattern.search(self.exp_time)
  780. if match is not None:
  781. flag[2] = True
  782. cur = cur.shift(days=-2)
  783. rule = r"昨"
  784. pattern = re.compile(rule)
  785. match = pattern.search(self.exp_time)
  786. if match is not None:
  787. flag[2] = True
  788. cur = cur.shift(days=-1)
  789. rule = r"今(?!年)"
  790. pattern = re.compile(rule)
  791. match = pattern.search(self.exp_time)
  792. if match is not None:
  793. flag[2] = True
  794. cur = cur.shift(days=0)
  795. rule = r"明(?!年)"
  796. pattern = re.compile(rule)
  797. match = pattern.search(self.exp_time)
  798. if match is not None:
  799. flag[2] = True
  800. cur = cur.shift(days=1)
  801. rule = r"(?<!大)后天"
  802. pattern = re.compile(rule)
  803. match = pattern.search(self.exp_time)
  804. if match is not None:
  805. flag[2] = True
  806. cur = cur.shift(days=2)
  807. rule = r"大*大后天"
  808. pattern = re.compile(rule)
  809. match = pattern.search(self.exp_time)
  810. if match is not None:
  811. rule = "大"
  812. pattern = re.compile(rule)
  813. match = pattern.findall(self.exp_time)
  814. flag[2] = True
  815. cur = cur.shift(days=(2 + len(match)))
  816. # todo 补充星期相关的预测 done
  817. rule = r"(?<=(上*上上(周|星期)))[1-7]?"
  818. pattern = re.compile(rule)
  819. match = pattern.search(self.exp_time)
  820. if match is not None:
  821. flag[2] = True
  822. try:
  823. week = int(match.group())
  824. except Exception:
  825. week = 1
  826. week -= 1
  827. span = week - cur.weekday()
  828. rule = "上"
  829. pattern = re.compile(rule)
  830. match = pattern.findall(self.exp_time)
  831. cur = cur.shift(weeks=-len(match), days=span)
  832. rule = r"(?<=((?<!上)上(周|星期)))[1-7]?"
  833. pattern = re.compile(rule)
  834. match = pattern.search(self.exp_time)
  835. if match is not None:
  836. flag[2] = True
  837. try:
  838. week = int(match.group())
  839. except Exception:
  840. week = 1
  841. week -= 1
  842. span = week - cur.weekday()
  843. cur = cur.shift(weeks=-1, days=span)
  844. rule = r"(?<=((?<!下)下(周|星期)))[1-7]?"
  845. pattern = re.compile(rule)
  846. match = pattern.search(self.exp_time)
  847. if match is not None:
  848. flag[2] = True
  849. try:
  850. week = int(match.group())
  851. except Exception:
  852. week = 1
  853. week -= 1
  854. span = week - cur.weekday()
  855. cur = cur.shift(weeks=1, days=span)
  856. # 这里对下下下周的时间转换做出了改善
  857. rule = r"(?<=(下*下下(周|星期)))[1-7]?"
  858. pattern = re.compile(rule)
  859. match = pattern.search(self.exp_time)
  860. if match is not None:
  861. flag[2] = True
  862. try:
  863. week = int(match.group())
  864. except Exception:
  865. week = 1
  866. week -= 1
  867. span = week - cur.weekday()
  868. rule = "下"
  869. pattern = re.compile(rule)
  870. match = pattern.findall(self.exp_time)
  871. cur = cur.shift(weeks=len(match), days=span)
  872. rule = r"(?<=((?<!(上|下|个|[0-9]))(周|星期)))[1-7]"
  873. pattern = re.compile(rule)
  874. match = pattern.search(self.exp_time)
  875. if match is not None:
  876. flag[2] = True
  877. try:
  878. week = int(match.group())
  879. except Exception:
  880. week = 1
  881. week -= 1
  882. span = week - cur.weekday()
  883. cur = cur.shift(days=span)
  884. # 处理未来时间
  885. cur = self.preferFutureWeek(week, cur)
  886. if flag[0] or flag[1] or flag[2]:
  887. self.tp.year = int(cur.year)
  888. if flag[1] or flag[2]:
  889. self.tp.month = int(cur.month)
  890. if flag[2]:
  891. self.tp.day = int(cur.day)
  892. def modifyTimeBase(self):
  893. """
  894. 该方法用于更新baseTime使之具有上下文关联性
  895. """
  896. if not self.normalizer.isTimeDelta:
  897. if 30 <= self.tp.year < 100:
  898. self.tp.year = 1900 + self.tp.year
  899. if 0 < self.tp.year < 30:
  900. self.tp.year = 2000 + self.tp.year
  901. time_grid = arrow2grid(self.normalizer.baseTime)
  902. arr = []
  903. for i in range(0, 6):
  904. if self.tp.tunit[i] == -1:
  905. arr.append(str(time_grid[i]))
  906. else:
  907. arr.append(str(self.tp.tunit[i]))
  908. self.normalizer.baseTime = grid2arrow(arr)
  909. def preferFutureWeek(self, weekday, cur):
  910. # 1. 确认用户选项
  911. if not self.normalizer.isPreferFuture:
  912. return cur
  913. # 2. 检查被检查的时间级别之前,是否没有更高级的已经确定的时间,如果有,则不进行处理.
  914. for i in range(0, 2):
  915. if self.tp.tunit[i] != -1:
  916. return cur
  917. # 获取当前是在周几,如果识别到的时间小于当前时间,则识别时间为下一周
  918. tmp = self.normalizer.baseTime
  919. curWeekday = tmp.weekday()
  920. if curWeekday > weekday:
  921. cur = cur.shift(days=7)
  922. return cur
  923. def preferFuture(self, checkTimeIndex):
  924. """
  925. 如果用户选项是倾向于未来时间,检查checkTimeIndex所指的时间是否是过去的时间,如果是的话,将大一级的时间设为当前时间的+1。
  926. 如在晚上说“早上8点看书”,则识别为明天早上;
  927. 12月31日说“3号买菜”,则识别为明年1月的3号。
  928. :param checkTimeIndex: _tp.tunit时间数组的下标
  929. """
  930. # 1. 检查被检查的时间级别之前,是否没有更高级的已经确定的时间,如果有,则不进行处理.
  931. for i in range(0, checkTimeIndex):
  932. if self.tp.tunit[i] != -1:
  933. return
  934. # 2. 根据上下文补充时间
  935. self.checkContextTime(checkTimeIndex)
  936. # 3. 根据上下文补充时间后再次检查被检查的时间级别之前,是否没有更高级的已经确定的时间,如果有,则不进行倾向处理
  937. # TODO 确认是否可以删除掉.
  938. # for i in range(0, checkTimeIndex):
  939. # if self.tp.tunit[i] != -1:
  940. # return
  941. # 4. 确认用户选项
  942. if not self.normalizer.isPreferFuture:
  943. return
  944. # 5. 获取当前时间,如果识别到的时间小于当前时间,则将其上的所有级别时间设置为当前时间,并且其上一级的时间步长+1
  945. time_arr = arrow2grid(self.normalizer.baseTime)
  946. cur = self.normalizer.baseTime
  947. cur_unit = int(time_arr[checkTimeIndex])
  948. # logger.debug(time_arr)
  949. # logger.debug(self.tp.tunit)
  950. if self.tp.year == -1:
  951. self._noyear = True
  952. else:
  953. self._noyear = False
  954. if cur_unit < self.tp.tunit[checkTimeIndex]:
  955. return
  956. # if cur_unit == self.tp.tunit[checkTimeIndex]:
  957. # down_unit = int(time_arr[checkTimeIndex + 1])
  958. # if down_unit
  959. # 准备增加的时间单位是被检查的时间的上一级,将上一级时间+1
  960. cur = self.addTime(cur, checkTimeIndex - 1)
  961. time_arr = arrow2grid(cur)
  962. for i in range(0, checkTimeIndex):
  963. self.tp.tunit[i] = int(time_arr[i])
  964. # if i == 1:
  965. # self.tp.tunit[i] += 1
  966. def _check_time(self, parse):
  967. """
  968. 检查未来时间点
  969. :param parse: 解析出来的list
  970. """
  971. time_arr = arrow2grid(self.normalizer.baseTime)
  972. if self._noyear:
  973. # check the month
  974. # logger.debug(parse)
  975. # logger.debug(time_arr)
  976. if parse[1] == int(time_arr[1]):
  977. if parse[2] > int(time_arr[2]):
  978. parse[0] = parse[0] - 1
  979. self._noyear = False
  980. def checkContextTime(self, checkTimeIndex: int):
  981. """
  982. 根据上下文时间补充时间信息
  983. :param checkTimeIndex:
  984. """
  985. if not self.isFirstTimeSolveContext:
  986. return
  987. for i in range(0, checkTimeIndex):
  988. if self.tp.tunit[i] == -1 and self.tp_origin.tunit[i] != -1:
  989. self.tp.tunit[i] = self.tp_origin.tunit[i]
  990. # 在处理小时这个级别时,如果上文时间是下午的且下文没有主动声明小时级别以上的时间,则也把下文时间设为下午
  991. if (
  992. self.isFirstTimeSolveContext is True
  993. and checkTimeIndex == 3
  994. and self.tp_origin.tunit[checkTimeIndex] >= 12 > self.tp.tunit[checkTimeIndex]
  995. ):
  996. self.tp.tunit[checkTimeIndex] += 12
  997. self.isFirstTimeSolveContext = False
  998. def addTime(self, cur: arrow.Arrow, fore_unit: int) -> arrow.Arrow:
  999. if fore_unit == 0:
  1000. cur = cur.shift(years=1)
  1001. elif fore_unit == 1:
  1002. cur = cur.shift(months=1)
  1003. elif fore_unit == 2:
  1004. cur = cur.shift(days=1)
  1005. elif fore_unit == 3:
  1006. cur = cur.shift(hours=1)
  1007. elif fore_unit == 4:
  1008. cur = cur.shift(minutes=1)
  1009. elif fore_unit == 5:
  1010. cur = cur.shift(seconds=1)
  1011. return cur