from typing import Callable
from datetime import datetime


def between_dates(
    start_month: int, start_day: int, end_month: int, end_day: int
) -> Callable[[datetime], bool]:
    """
    Filter that allows events between specific dates in each year.

    For example, `between_dates(10, 15, 3, 31)` would only allow dates between October 15
    and March 31 in any year.

    Args:
        - start_month (int): the starting month, as a number (1 = January)
        - start_day (int): the starting day, as a number
        - end_month (int): the ending month, as a number
        - end_day (int): the ending day, as a number

    Returns:
        - Callable[[datetime], bool]: a filter function
    """

    def _filter_fn(dt: datetime) -> bool:
        date = (dt.month, dt.day)

        # if the start is before the end, then these reflect dates in the same year
        if start_month <= end_month:
            return date >= (start_month, start_day) and date <= (end_month, end_day)
        # otherwise they represent dates across two years
        else:
            return date >= (start_month, start_day) or date <= (end_month, end_day)

    return _filter_fn