cron_util.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. # -*- coding: utf-8 -*-
  2. import re
  3. from datetime import datetime
  4. class CronUtil:
  5. """
  6. Cron表达式工具类
  7. """
  8. @classmethod
  9. def __valid_range(cls, search_str: str, start_range: int, end_range: int) -> bool:
  10. """
  11. 校验范围表达式的合法性。
  12. 参数:
  13. - search_str (str): 范围表达式。
  14. - start_range (int): 开始范围。
  15. - end_range (int): 结束范围。
  16. 返回:
  17. - bool: 校验是否通过。
  18. """
  19. match = re.match(r'^(\d+)-(\d+)$', search_str)
  20. if match:
  21. start, end = int(match.group(1)), int(match.group(2))
  22. return start_range <= start < end <= end_range
  23. return False
  24. @classmethod
  25. def __valid_sum(cls, search_str: str, start_range_a: int, start_range_b: int, end_range_a: int, end_range_b: int, sum_range: int) -> bool:
  26. """
  27. 校验和表达式的合法性。
  28. 参数:
  29. - search_str (str): 和表达式。
  30. - start_range_a (int): 开始范围A。
  31. - start_range_b (int): 开始范围B。
  32. - end_range_a (int): 结束范围A。
  33. - end_range_b (int): 结束范围B。
  34. - sum_range (int): 总和范围。
  35. 返回:
  36. - bool: 校验是否通过。
  37. """
  38. match = re.match(r'^(\d+)/(\d+)$', search_str)
  39. if match:
  40. start, end = int(match.group(1)), int(match.group(2))
  41. return (
  42. start_range_a <= start <= start_range_b
  43. and end_range_a <= end <= end_range_b
  44. and start + end <= sum_range
  45. )
  46. return False
  47. @classmethod
  48. def validate_second_or_minute(cls, second_or_minute: str) -> bool:
  49. """
  50. 校验秒或分钟字段的合法性。
  51. 参数:
  52. - second_or_minute (str): 秒或分钟值。
  53. 返回:
  54. - bool: 校验是否通过。
  55. """
  56. if (
  57. second_or_minute == '*'
  58. or ('-' in second_or_minute and cls.__valid_range(second_or_minute, 0, 59))
  59. or ('/' in second_or_minute and cls.__valid_sum(second_or_minute, 0, 58, 1, 59, 59))
  60. or re.match(r'^(?:[0-5]?\d|59)(?:,[0-5]?\d|59)*$', second_or_minute)
  61. ):
  62. return True
  63. return False
  64. @classmethod
  65. def validate_hour(cls, hour: str) -> bool:
  66. """
  67. 校验小时字段的合法性。
  68. 参数:
  69. - hour (str): 小时值。
  70. 返回:
  71. - bool: 校验是否通过。
  72. """
  73. if (
  74. hour == '*'
  75. or ('-' in hour and cls.__valid_range(hour, 0, 23))
  76. or ('/' in hour and cls.__valid_sum(hour, 0, 22, 1, 23, 23))
  77. or re.match(r'^(?:0|[1-9]|1\d|2[0-3])(?:,(?:0|[1-9]|1\d|2[0-3]))*$', hour)
  78. ):
  79. return True
  80. return False
  81. @classmethod
  82. def validate_day(cls, day: str) -> bool:
  83. """
  84. 校验日期字段的合法性。
  85. 参数:
  86. - day (str): 日值。
  87. 返回:
  88. - bool: 校验是否通过。
  89. """
  90. if (
  91. day in ['*', '?', 'L']
  92. or ('-' in day and cls.__valid_range(day, 1, 31))
  93. or ('/' in day and cls.__valid_sum(day, 1, 30, 1, 30, 31))
  94. or ('W' in day and re.match(r'^(?:[1-9]|1\d|2\d|3[01])W$', day))
  95. or re.match(r'^(?:0|[1-9]|1\d|2[0-9]|3[0-1])(?:,(?:0|[1-9]|1\d|2[0-9]|3[0-1]))*$', day)
  96. ):
  97. return True
  98. return False
  99. @classmethod
  100. def validate_month(cls, month: str) -> bool:
  101. """
  102. 校验月份字段的合法性。
  103. 参数:
  104. - month (str): 月值。
  105. 返回:
  106. - bool: 校验是否通过。
  107. """
  108. if (
  109. month == '*'
  110. or ('-' in month and cls.__valid_range(month, 1, 12))
  111. or ('/' in month and cls.__valid_sum(month, 1, 11, 1, 11, 12))
  112. or re.match(r'^(?:0|[1-9]|1[0-2])(?:,(?:0|[1-9]|1[0-2]))*$', month)
  113. ):
  114. return True
  115. return False
  116. @classmethod
  117. def validate_week(cls, week: str) -> bool:
  118. """
  119. 校验星期字段的合法性。
  120. 参数:
  121. - week (str): 周值。
  122. 返回:
  123. - bool: 校验是否通过。
  124. """
  125. if (
  126. week in ['*', '?']
  127. or ('-' in week and cls.__valid_range(week, 1, 7))
  128. or ('#' in week and re.match(r'^[1-7]#[1-4]$', week))
  129. or ('L' in week and re.match(r'^[1-7]L$', week))
  130. or re.match(r'^[1-7](?:(,[1-7]))*$', week)
  131. ):
  132. return True
  133. return False
  134. @classmethod
  135. def validate_year(cls, year: str) -> bool:
  136. """
  137. 校验年份字段的合法性。
  138. 参数:
  139. - year (str): 年值。
  140. 返回:
  141. - bool: 校验是否通过。
  142. """
  143. current_year = int(datetime.now().year)
  144. future_years = [current_year + i for i in range(9)]
  145. if (
  146. year == '*'
  147. or ('-' in year and cls.__valid_range(year, current_year, 2099))
  148. or ('/' in year and cls.__valid_sum(year, current_year, 2098, 1, 2099 - current_year, 2099))
  149. or ('#' in year and re.match(r'^[1-7]#[1-4]$', year))
  150. or ('L' in year and re.match(r'^[1-7]L$', year))
  151. or (
  152. (len(year) == 4 or ',' in year)
  153. and all(int(item) in future_years and current_year <= int(item) <= 2099 for item in year.split(','))
  154. )
  155. ):
  156. return True
  157. return False
  158. @classmethod
  159. def validate_cron_expression(cls, cron_expression: str) -> bool | None:
  160. """
  161. 校验 Cron 表达式是否正确。
  162. 参数:
  163. - cron_expression (str): Cron 表达式。
  164. 返回:
  165. - bool | None: 校验是否通过。
  166. """
  167. values = cron_expression.split()
  168. if len(values) != 6 and len(values) != 7:
  169. return False
  170. second_validation = cls.validate_second_or_minute(values[0])
  171. minute_validation = cls.validate_second_or_minute(values[1])
  172. hour_validation = cls.validate_hour(values[2])
  173. day_validation = cls.validate_day(values[3])
  174. month_validation = cls.validate_month(values[4])
  175. week_validation = cls.validate_week(values[5])
  176. validation = (
  177. second_validation
  178. and minute_validation
  179. and hour_validation
  180. and day_validation
  181. and month_validation
  182. and week_validation
  183. )
  184. if len(values) == 6:
  185. return validation
  186. if len(values) == 7:
  187. year_validation = cls.validate_year(values[6])
  188. return validation and year_validation