CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-croniter

croniter provides iteration for datetime object with cron like format

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

validation-matching.mddocs/

Validation and Matching

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.

Capabilities

Expression Validation

Validate cron expressions for syntax correctness and supported features.

@classmethod
def is_valid(
    cls,
    expression: str,
    hash_id=None,
    encoding="UTF-8",
    second_at_beginning=False,
) -> bool:
    """
    Validate if a cron expression is syntactically correct and supported.
    
    Parameters:
    - expression: Cron expression string to validate
    - hash_id: Hash ID for hashed expressions (bytes or str)
    - encoding: Character encoding for hash_id if provided as string
    - second_at_beginning: Whether seconds field is at beginning instead of end
    
    Returns:
    True if expression is valid, False otherwise
    """

Single Datetime Matching

Test if a specific datetime matches a cron expression.

@classmethod
def match(cls, cron_expression: str, testdate, day_or=True, second_at_beginning=False) -> bool:
    """
    Test if a datetime matches the given cron expression.
    
    Parameters:
    - cron_expression: Cron expression string
    - testdate: datetime object to test
    - day_or: How to handle day and day_of_week fields (True=OR, False=AND)
    - second_at_beginning: Whether seconds field is at beginning
    
    Returns:
    True if testdate matches the cron expression, False otherwise
    """

Range-Based Matching

Test if a cron expression has any matches within a datetime range.

@classmethod
def match_range(
    cls,
    cron_expression: str,
    from_datetime,
    to_datetime,
    day_or=True,
    second_at_beginning=False,
) -> bool:
    """
    Test if a cron expression matches within a datetime range.
    
    Parameters:
    - cron_expression: Cron expression string
    - from_datetime: Start of range (datetime object)
    - to_datetime: End of range (datetime object)  
    - day_or: How to handle day and day_of_week fields (True=OR, False=AND)
    - second_at_beginning: Whether seconds field is at beginning
    
    Returns:
    True if expression matches any time within the range, False otherwise
    """

Usage Examples

Basic Expression Validation

from croniter import croniter

# Valid expressions
print(croniter.is_valid('0 0 1 * *'))        # True - first day of month at midnight
print(croniter.is_valid('*/5 * * * *'))      # True - every 5 minutes
print(croniter.is_valid('0 9-17 * * 1-5'))   # True - business hours on weekdays

# Invalid expressions
print(croniter.is_valid('0 wrong_value 1 * *'))  # False - invalid hour value
print(croniter.is_valid('60 * * * *'))           # False - minute out of range
print(croniter.is_valid('* * 32 * *'))           # False - day out of range

Advanced Expression Validation

from croniter import croniter

# Second-level precision (6-field format)
print(croniter.is_valid('30 */5 * * * *'))           # True - every 5 minutes at 30 seconds
print(croniter.is_valid('*/15 * * * * *'))           # True - every 15 seconds

# Year field (7-field format)  
print(croniter.is_valid('0 0 1 1 * 0 2020/2'))      # True - Jan 1st every 2 years from 2020

# Hashed expressions
print(croniter.is_valid('H H * * *', hash_id="job1")) # True - Jenkins-style hash
print(croniter.is_valid('H(0-30) H(9-17) * * *'))    # True - hash with ranges

# Invalid advanced expressions
print(croniter.is_valid('0 0 1 1 * 0 2200'))        # False - year out of range (>2099)

Single Datetime Matching

from croniter import croniter
from datetime import datetime

# Test specific datetime against cron expressions
test_dt = datetime(2019, 1, 14, 0, 0, 0)  # January 14, 2019 at midnight

# Daily at midnight
print(croniter.match("0 0 * * *", test_dt))         # True - matches midnight

# Different time
test_dt2 = datetime(2019, 1, 14, 0, 2, 0)           # 00:02
print(croniter.match("0 0 * * *", test_dt2))        # False - doesn't match midnight

# Weekday check
test_dt3 = datetime(2019, 1, 14, 9, 0, 0)           # Monday morning
print(croniter.match("0 9 * * 1", test_dt3))        # True - 9 AM on Monday

# Complex expression with OR logic
test_dt4 = datetime(2019, 1, 1, 4, 2, 0)            # January 1st at 04:02
print(croniter.match("2 4 1 * wed", test_dt4))      # True - 1st of month (OR logic)

Day/Dayofweek Logic in Matching

from croniter import croniter
from datetime import datetime

# January 1st, 2019 was a Tuesday
test_dt = datetime(2019, 1, 1, 4, 2, 0)

# OR logic (default): match if it's 1st of month OR if it's Wednesday
print(croniter.match("2 4 1 * wed", test_dt, day_or=True))   # True - it's 1st of month

# AND logic: match only if it's 1st of month AND it's Wednesday  
print(croniter.match("2 4 1 * wed", test_dt, day_or=False))  # False - it's Tuesday, not Wednesday

# Test on a Wednesday that's not the 1st
wed_dt = datetime(2019, 1, 2, 4, 2, 0)  # January 2nd, 2019 (Wednesday)
print(croniter.match("2 4 1 * wed", wed_dt, day_or=True))    # True - it's Wednesday (OR logic)
print(croniter.match("2 4 1 * wed", wed_dt, day_or=False))   # False - not 1st of month (AND logic)

Range-Based Matching

from croniter import croniter
from datetime import datetime

# Test if expression matches within a time range
start = datetime(2019, 1, 13, 0, 59, 0)  # Before midnight
end = datetime(2019, 1, 14, 0, 1, 0)     # After midnight

# Daily at midnight - should match within this range
print(croniter.match_range("0 0 * * *", start, end))        # True

# Different time range that doesn't include midnight
start2 = datetime(2019, 1, 13, 0, 1, 0)
end2 = datetime(2019, 1, 13, 0, 59, 0)
print(croniter.match_range("0 0 * * *", start2, end2))      # False

# Business hours check
start3 = datetime(2019, 1, 1, 3, 2, 0)   # Before business hours  
end3 = datetime(2019, 1, 1, 5, 1, 0)     # Spans into business hours
print(croniter.match_range("2 4 1 * wed", start3, end3))    # True - 04:02 is in range

Second-Level Precision Matching

from croniter import croniter
from datetime import datetime

# Test with second-level precision
test_dt = datetime(2019, 1, 1, 0, 5, 30)  # 30 seconds past 5 minutes

# Every 30 seconds (6-field format)
print(croniter.match("30 * * * * *", test_dt))              # True

# Different second
test_dt2 = datetime(2019, 1, 1, 0, 5, 15)  # 15 seconds past 5 minutes  
print(croniter.match("30 * * * * *", test_dt2))             # False

# Seconds at beginning format
print(croniter.match("30 5 0 1 1 *", test_dt, second_at_beginning=True))  # True

Validation Error Handling

from croniter import croniter

# Validation catches various error types
invalid_expressions = [
    "invalid cron",       # General syntax error
    "* * * *",           # Too few fields  
    "60 * * * *",        # Minute out of range
    "* 25 * * *",        # Hour out of range
    "* * 32 * *",        # Day out of range
    "* * * 13 *",        # Month out of range
    "* * * * 8",         # Day of week out of range
    "* * * jan13 *",     # Invalid month name
]

for expr in invalid_expressions:
    is_valid = croniter.is_valid(expr)
    print(f"'{expr}': {is_valid}")

Hash ID Validation

from croniter import croniter

# Hashed expressions require hash_id for validation
print(croniter.is_valid("H H * * *"))                    # False - no hash_id
print(croniter.is_valid("H H * * *", hash_id="job1"))    # True - with hash_id

# Hash ID can be string or bytes
print(croniter.is_valid("H H * * *", hash_id=b"job1"))   # True - bytes hash_id
print(croniter.is_valid("H H * * *", hash_id="job1"))    # True - string hash_id

# Custom encoding for string hash_id
print(croniter.is_valid("H H * * *", hash_id="jöb1", encoding="utf-8"))  # True

Practical Validation Scenarios

from croniter import croniter
from datetime import datetime

def validate_cron_schedule(expression, test_datetime=None):
    """Validate a cron expression and optionally test against a datetime."""
    
    # Basic validation
    if not croniter.is_valid(expression):
        return {"valid": False, "error": "Invalid cron expression syntax"}
    
    # Test against specific datetime if provided
    if test_datetime:
        matches = croniter.match(expression, test_datetime)
        return {
            "valid": True, 
            "matches_test_time": matches,
            "test_time": test_datetime
        }
    
    return {"valid": True}

# Example usage
result1 = validate_cron_schedule("0 9 * * 1-5")  # Valid weekday morning expression
result2 = validate_cron_schedule("0 9 * * 1-5", datetime(2019, 1, 14, 9, 0))  # Monday 9 AM
result3 = validate_cron_schedule("invalid expression")

print(result1)  # {'valid': True}
print(result2)  # {'valid': True, 'matches_test_time': True, 'test_time': datetime(...)}
print(result3)  # {'valid': False, 'error': 'Invalid cron expression syntax'}

Install with Tessl CLI

npx tessl i tessl/pypi-croniter

docs

advanced-features.md

core-iteration.md

index.md

range-operations.md

validation-matching.md

tile.json