croniter provides iteration for datetime object with cron like format
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Range-based iteration functionality similar to Python's built-in range() function, but for datetime objects using cron expressions as the "step" parameter. This allows you to find all datetime matches within a specific time range.
Generate all datetime matches within a specified range using a cron expression.
def croniter_range(
start,
stop,
expr_format: str,
ret_type=None,
day_or=True,
exclude_ends=False,
_croniter=None,
second_at_beginning=False,
expand_from_start_time=False,
):
"""
Generator that provides all times from start to stop matching the given cron expression.
You can think of this function as sibling to the builtin range function for datetime objects.
Like range(start,stop,step), except that here 'step' is a cron expression.
Parameters:
- start: Start datetime (datetime object or timestamp)
- stop: End datetime (datetime object or timestamp)
- expr_format: Cron expression string
- ret_type: Return type for yielded values (float, datetime, or None for auto-detect)
- day_or: How to handle day and day_of_week fields (True=OR, False=AND)
- exclude_ends: If True, exclude start/stop times even if they match (default False)
- _croniter: Custom croniter class (for testing/customization)
- second_at_beginning: Put seconds field at beginning instead of end
- expand_from_start_time: Calculate intervals from start_time instead of calendar
Yields:
All matching datetimes between start and stop (inclusive by default)
"""from croniter import croniter_range
from datetime import datetime
# Find all first Saturdays of each month in 2019
start = datetime(2019, 1, 1)
stop = datetime(2019, 12, 31)
for dt in croniter_range(start, stop, "0 0 * * sat#1"):
print(dt)
# 2019-01-05 00:00:00
# 2019-02-02 00:00:00
# 2019-03-02 00:00:00
# ... (continues for each month)import time
from datetime import datetime
# Use timestamps for input
start_ts = time.mktime(datetime(2019, 1, 1).timetuple())
stop_ts = time.mktime(datetime(2019, 1, 31).timetuple())
# Find all weekdays at 9 AM in January 2019
for dt in croniter_range(start_ts, stop_ts, "0 9 * * 1-5"):
print(datetime.fromtimestamp(dt))from datetime import datetime
start = datetime(2019, 1, 1, 0, 0, 0) # Exactly midnight
stop = datetime(2019, 1, 2, 0, 0, 0) # Exactly next midnight
# Without exclude_ends (default): includes both endpoints if they match
daily_matches = list(croniter_range(start, stop, "0 0 * * *"))
print(len(daily_matches)) # 2 (both midnight times)
# With exclude_ends: excludes endpoints even if they match
daily_matches = list(croniter_range(start, stop, "0 0 * * *", exclude_ends=True))
print(len(daily_matches)) # 0 (no matches between the endpoints)from datetime import datetime
# Croniter_range can work with reverse time order (stop before start)
start = datetime(2019, 1, 31)
stop = datetime(2019, 1, 1)
# This will work and yield dates in reverse chronological order
for dt in croniter_range(start, stop, "0 12 * * *"): # Noon every day
print(dt)
# 2019-01-31 12:00:00
# 2019-01-30 12:00:00
# ... (continues backward)
# 2019-01-01 12:00:00from datetime import datetime
start = datetime(2019, 1, 1)
stop = datetime(2019, 12, 31)
# Every weekday at 9:30 AM and 2:30 PM
for dt in croniter_range(start, stop, "30 9,14 * * 1-5"):
print(dt)
# Last Friday of each month at 5 PM
for dt in croniter_range(start, stop, "0 17 * * L5"):
print(dt)
# Every 15 minutes during business hours (9 AM - 5 PM) on weekdays
for dt in croniter_range(start, stop, "*/15 9-17 * * 1-5"):
print(dt)from datetime import datetime
start = datetime(2019, 1, 1)
stop = datetime(2019, 1, 7)
# Return as datetime objects
datetime_results = list(croniter_range(start, stop, "0 0 * * *", ret_type=datetime))
print(type(datetime_results[0])) # <class 'datetime.datetime'>
# Return as timestamps
timestamp_results = list(croniter_range(start, stop, "0 0 * * *", ret_type=float))
print(type(timestamp_results[0])) # <class 'float'>
# Auto-detect based on input types (default behavior)
auto_results = list(croniter_range(start, stop, "0 0 * * *"))
print(type(auto_results[0])) # <class 'datetime.datetime'> (matches input type)from datetime import datetime
start = datetime(2019, 1, 1, 0, 0, 0)
stop = datetime(2019, 1, 1, 0, 1, 0) # One minute range
# Every 15 seconds within the minute
for dt in croniter_range(start, stop, "*/15 * * * * *"): # 6-field format with seconds
print(dt)
# 2019-01-01 00:00:00
# 2019-01-01 00:00:15
# 2019-01-01 00:00:30
# 2019-01-01 00:00:45
# 2019-01-01 00:01:00from datetime import datetime
start = datetime(2019, 6, 1)
stop = datetime(2019, 6, 30)
# OR logic (default): 1st day of month OR any Wednesday
or_results = list(croniter_range(start, stop, "0 0 1 * wed", day_or=True))
# AND logic: 1st day of month IF it's a Wednesday
and_results = list(croniter_range(start, stop, "0 0 1 * wed", day_or=False))
print(f"OR results: {len(or_results)}") # More matches
print(f"AND results: {len(and_results)}") # Fewer matches (only if 1st is Wednesday)from datetime import datetime
# For very large date ranges or sparse expressions,
# croniter_range inherits performance limits from croniter class
start = datetime(2000, 1, 1)
stop = datetime(2050, 12, 31)
# Very sparse expression - might hit performance limits
try:
sparse_matches = list(croniter_range(
start, stop,
"0 4 1 1 fri", # 4 AM on January 1st if it's Friday
day_or=False
))
print(f"Found {len(sparse_matches)} matches")
except Exception as e:
print(f"Performance limit reached: {e}")from datetime import datetime
start = datetime(2024, 7, 11) # Start on July 11th
stop = datetime(2024, 10, 1)
# Default behavior: every 7 days from calendar start (1st, 8th, 15th, etc.)
calendar_matches = list(croniter_range(start, stop, "0 0 */7 * *"))
# Custom expansion: every 7 days from start_time (11th, 18th, 25th, etc.)
custom_matches = list(croniter_range(
start, stop,
"0 0 */7 * *",
expand_from_start_time=True
))
print("Calendar-based:", [dt.day for dt in calendar_matches[:5]])
print("Start-time-based:", [dt.day for dt in custom_matches[:5]])Install with Tessl CLI
npx tessl i tessl/pypi-croniter