cron_util.py 6.6 KB

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