| 232 | if (!parts) return false; |
| 233 | |
| 234 | const validateField = (field: string, min: number, max: number): boolean => { |
| 235 | if (field === '*') return true; |
| 236 | |
| 237 | // Handle step values (*/n or n/m) |
| 238 | if (field.includes('/')) { |
| 239 | const [range, step] = field.split('/'); |
| 240 | if (range !== '*' && !validateField(range, min, max)) return false; |
| 241 | const stepNum = parseInt(step); |
| 242 | return !isNaN(stepNum) && stepNum > 0; |
| 243 | } |
| 244 | |
| 245 | // Handle ranges (n-m) |
| 246 | if (field.includes('-')) { |
| 247 | const [start, end] = field.split('-').map(Number); |
| 248 | return !isNaN(start) && !isNaN(end) && start >= min && end <= max && start <= end; |
| 249 | } |
| 250 | |
| 251 | // Handle lists (n,m,...) |
| 252 | if (field.includes(',')) { |
| 253 | return field.split(',').every(v => { |
| 254 | const num = parseInt(v); |
| 255 | return !isNaN(num) && num >= min && num <= max; |
| 256 | }); |
| 257 | } |
| 258 | |
| 259 | // Single value |
| 260 | const num = parseInt(field); |
| 261 | return !isNaN(num) && num >= min && num <= max; |
| 262 | }; |
| 263 | |
| 264 | return ( |
| 265 | validateField(parts.minute, 0, 59) && |