0
# Validation and Matching
1
2
Comprehensive validation of cron expressions and testing whether specific datetime objects match cron patterns. This includes both single datetime matching and range-based matching capabilities.
3
4
## Capabilities
5
6
### Expression Validation
7
8
Validate cron expressions for syntax correctness and supported features.
9
10
```python { .api }
11
@classmethod
12
def is_valid(
13
cls,
14
expression: str,
15
hash_id=None,
16
encoding="UTF-8",
17
second_at_beginning=False,
18
) -> bool:
19
"""
20
Validate if a cron expression is syntactically correct and supported.
21
22
Parameters:
23
- expression: Cron expression string to validate
24
- hash_id: Hash ID for hashed expressions (bytes or str)
25
- encoding: Character encoding for hash_id if provided as string
26
- second_at_beginning: Whether seconds field is at beginning instead of end
27
28
Returns:
29
True if expression is valid, False otherwise
30
"""
31
```
32
33
### Single Datetime Matching
34
35
Test if a specific datetime matches a cron expression.
36
37
```python { .api }
38
@classmethod
39
def match(cls, cron_expression: str, testdate, day_or=True, second_at_beginning=False) -> bool:
40
"""
41
Test if a datetime matches the given cron expression.
42
43
Parameters:
44
- cron_expression: Cron expression string
45
- testdate: datetime object to test
46
- day_or: How to handle day and day_of_week fields (True=OR, False=AND)
47
- second_at_beginning: Whether seconds field is at beginning
48
49
Returns:
50
True if testdate matches the cron expression, False otherwise
51
"""
52
```
53
54
### Range-Based Matching
55
56
Test if a cron expression has any matches within a datetime range.
57
58
```python { .api }
59
@classmethod
60
def match_range(
61
cls,
62
cron_expression: str,
63
from_datetime,
64
to_datetime,
65
day_or=True,
66
second_at_beginning=False,
67
) -> bool:
68
"""
69
Test if a cron expression matches within a datetime range.
70
71
Parameters:
72
- cron_expression: Cron expression string
73
- from_datetime: Start of range (datetime object)
74
- to_datetime: End of range (datetime object)
75
- day_or: How to handle day and day_of_week fields (True=OR, False=AND)
76
- second_at_beginning: Whether seconds field is at beginning
77
78
Returns:
79
True if expression matches any time within the range, False otherwise
80
"""
81
```
82
83
## Usage Examples
84
85
### Basic Expression Validation
86
87
```python
88
from croniter import croniter
89
90
# Valid expressions
91
print(croniter.is_valid('0 0 1 * *')) # True - first day of month at midnight
92
print(croniter.is_valid('*/5 * * * *')) # True - every 5 minutes
93
print(croniter.is_valid('0 9-17 * * 1-5')) # True - business hours on weekdays
94
95
# Invalid expressions
96
print(croniter.is_valid('0 wrong_value 1 * *')) # False - invalid hour value
97
print(croniter.is_valid('60 * * * *')) # False - minute out of range
98
print(croniter.is_valid('* * 32 * *')) # False - day out of range
99
```
100
101
### Advanced Expression Validation
102
103
```python
104
from croniter import croniter
105
106
# Second-level precision (6-field format)
107
print(croniter.is_valid('30 */5 * * * *')) # True - every 5 minutes at 30 seconds
108
print(croniter.is_valid('*/15 * * * * *')) # True - every 15 seconds
109
110
# Year field (7-field format)
111
print(croniter.is_valid('0 0 1 1 * 0 2020/2')) # True - Jan 1st every 2 years from 2020
112
113
# Hashed expressions
114
print(croniter.is_valid('H H * * *', hash_id="job1")) # True - Jenkins-style hash
115
print(croniter.is_valid('H(0-30) H(9-17) * * *')) # True - hash with ranges
116
117
# Invalid advanced expressions
118
print(croniter.is_valid('0 0 1 1 * 0 2200')) # False - year out of range (>2099)
119
```
120
121
### Single Datetime Matching
122
123
```python
124
from croniter import croniter
125
from datetime import datetime
126
127
# Test specific datetime against cron expressions
128
test_dt = datetime(2019, 1, 14, 0, 0, 0) # January 14, 2019 at midnight
129
130
# Daily at midnight
131
print(croniter.match("0 0 * * *", test_dt)) # True - matches midnight
132
133
# Different time
134
test_dt2 = datetime(2019, 1, 14, 0, 2, 0) # 00:02
135
print(croniter.match("0 0 * * *", test_dt2)) # False - doesn't match midnight
136
137
# Weekday check
138
test_dt3 = datetime(2019, 1, 14, 9, 0, 0) # Monday morning
139
print(croniter.match("0 9 * * 1", test_dt3)) # True - 9 AM on Monday
140
141
# Complex expression with OR logic
142
test_dt4 = datetime(2019, 1, 1, 4, 2, 0) # January 1st at 04:02
143
print(croniter.match("2 4 1 * wed", test_dt4)) # True - 1st of month (OR logic)
144
```
145
146
### Day/Dayofweek Logic in Matching
147
148
```python
149
from croniter import croniter
150
from datetime import datetime
151
152
# January 1st, 2019 was a Tuesday
153
test_dt = datetime(2019, 1, 1, 4, 2, 0)
154
155
# OR logic (default): match if it's 1st of month OR if it's Wednesday
156
print(croniter.match("2 4 1 * wed", test_dt, day_or=True)) # True - it's 1st of month
157
158
# AND logic: match only if it's 1st of month AND it's Wednesday
159
print(croniter.match("2 4 1 * wed", test_dt, day_or=False)) # False - it's Tuesday, not Wednesday
160
161
# Test on a Wednesday that's not the 1st
162
wed_dt = datetime(2019, 1, 2, 4, 2, 0) # January 2nd, 2019 (Wednesday)
163
print(croniter.match("2 4 1 * wed", wed_dt, day_or=True)) # True - it's Wednesday (OR logic)
164
print(croniter.match("2 4 1 * wed", wed_dt, day_or=False)) # False - not 1st of month (AND logic)
165
```
166
167
### Range-Based Matching
168
169
```python
170
from croniter import croniter
171
from datetime import datetime
172
173
# Test if expression matches within a time range
174
start = datetime(2019, 1, 13, 0, 59, 0) # Before midnight
175
end = datetime(2019, 1, 14, 0, 1, 0) # After midnight
176
177
# Daily at midnight - should match within this range
178
print(croniter.match_range("0 0 * * *", start, end)) # True
179
180
# Different time range that doesn't include midnight
181
start2 = datetime(2019, 1, 13, 0, 1, 0)
182
end2 = datetime(2019, 1, 13, 0, 59, 0)
183
print(croniter.match_range("0 0 * * *", start2, end2)) # False
184
185
# Business hours check
186
start3 = datetime(2019, 1, 1, 3, 2, 0) # Before business hours
187
end3 = datetime(2019, 1, 1, 5, 1, 0) # Spans into business hours
188
print(croniter.match_range("2 4 1 * wed", start3, end3)) # True - 04:02 is in range
189
```
190
191
### Second-Level Precision Matching
192
193
```python
194
from croniter import croniter
195
from datetime import datetime
196
197
# Test with second-level precision
198
test_dt = datetime(2019, 1, 1, 0, 5, 30) # 30 seconds past 5 minutes
199
200
# Every 30 seconds (6-field format)
201
print(croniter.match("30 * * * * *", test_dt)) # True
202
203
# Different second
204
test_dt2 = datetime(2019, 1, 1, 0, 5, 15) # 15 seconds past 5 minutes
205
print(croniter.match("30 * * * * *", test_dt2)) # False
206
207
# Seconds at beginning format
208
print(croniter.match("30 5 0 1 1 *", test_dt, second_at_beginning=True)) # True
209
```
210
211
### Validation Error Handling
212
213
```python
214
from croniter import croniter
215
216
# Validation catches various error types
217
invalid_expressions = [
218
"invalid cron", # General syntax error
219
"* * * *", # Too few fields
220
"60 * * * *", # Minute out of range
221
"* 25 * * *", # Hour out of range
222
"* * 32 * *", # Day out of range
223
"* * * 13 *", # Month out of range
224
"* * * * 8", # Day of week out of range
225
"* * * jan13 *", # Invalid month name
226
]
227
228
for expr in invalid_expressions:
229
is_valid = croniter.is_valid(expr)
230
print(f"'{expr}': {is_valid}")
231
```
232
233
### Hash ID Validation
234
235
```python
236
from croniter import croniter
237
238
# Hashed expressions require hash_id for validation
239
print(croniter.is_valid("H H * * *")) # False - no hash_id
240
print(croniter.is_valid("H H * * *", hash_id="job1")) # True - with hash_id
241
242
# Hash ID can be string or bytes
243
print(croniter.is_valid("H H * * *", hash_id=b"job1")) # True - bytes hash_id
244
print(croniter.is_valid("H H * * *", hash_id="job1")) # True - string hash_id
245
246
# Custom encoding for string hash_id
247
print(croniter.is_valid("H H * * *", hash_id="jöb1", encoding="utf-8")) # True
248
```
249
250
### Practical Validation Scenarios
251
252
```python
253
from croniter import croniter
254
from datetime import datetime
255
256
def validate_cron_schedule(expression, test_datetime=None):
257
"""Validate a cron expression and optionally test against a datetime."""
258
259
# Basic validation
260
if not croniter.is_valid(expression):
261
return {"valid": False, "error": "Invalid cron expression syntax"}
262
263
# Test against specific datetime if provided
264
if test_datetime:
265
matches = croniter.match(expression, test_datetime)
266
return {
267
"valid": True,
268
"matches_test_time": matches,
269
"test_time": test_datetime
270
}
271
272
return {"valid": True}
273
274
# Example usage
275
result1 = validate_cron_schedule("0 9 * * 1-5") # Valid weekday morning expression
276
result2 = validate_cron_schedule("0 9 * * 1-5", datetime(2019, 1, 14, 9, 0)) # Monday 9 AM
277
result3 = validate_cron_schedule("invalid expression")
278
279
print(result1) # {'valid': True}
280
print(result2) # {'valid': True, 'matches_test_time': True, 'test_time': datetime(...)}
281
print(result3) # {'valid': False, 'error': 'Invalid cron expression syntax'}
282
```