Skip to content

Instantly share code, notes, and snippets.

@eddiebergman
Created March 10, 2025 09:14
Show Gist options
  • Save eddiebergman/98d50765aea88285895c45758a3c3e73 to your computer and use it in GitHub Desktop.
Save eddiebergman/98d50765aea88285895c45758a3c3e73 to your computer and use it in GitHub Desktop.
A Python `datetime` iterator
from datetime import datetime, timedelta
from typing import Iterator
def date_range(
start: datetime,
end: datetime,
step: timedelta,
*,
clamp: bool = True,
) -> Iterator[datetime]:
"""Generates date ranges.
Some important differences from `range()`.
* Includes `end` in the range if it aligns exactly.
* Does not support backward iteration.
!!! tip "Start and end pairs"
```python
import itertools
start = ...
end = ...
for start, end in iterools.pairwise(date_range(start, end, step)):
...
```
Args:
start (datetime): Start of the range.
end (datetime): End of the range.
step (timedelta): Step size.
clamp(bool, optional): Clamp the last range to the end.
Yields:
Iterator[tuple[datetime, datetime]]: Date range.
"""
if start > end:
raise ValueError(
"Start date must be less than or equal to end date."
f" Got {start=} and {end=}"
)
duration = end - start
q, r = divmod(duration, step)
n_steps = q + 1 + bool(r) # 1 for including endpoint, bool(r) if remainder
date_itr = (start + i * step for i in range(n_steps))
if clamp:
date_itr = (min(dt, end) for dt in date_itr)
yield from date_itr
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment