Functional Weave
Code in Python

charts.ticks@1.0.0

impl/python/time_tick_interval.py

1,411 bytes · the Python implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

from .charts_ticks_tick_step import tick_step  ← tickStep, another function of this group · built into the same file, even by a slim install
from .charts_ticks_types import TimeTickInterval
from .dates_add_days import epoch_day_from_iso  ← from dates.add-days ^1.0.0 · built alongside by fune

# d3-scale's calendar ladder, from a day up, with the lengths in days it
# compares against: a month counts as 30, a quarter as 90, a year as 365.
_LADDER = [
    ("day", 1, 1),
    ("day", 2, 2),
    ("week", 1, 7),
    ("month", 1, 30),
    ("quarter", 1, 90),
    ("year", 1, 365),
]


def time_tick_interval(start: str, stop: str, count: int) -> TimeTickInterval:
    """The calendar interval for about count ticks between two dates, as d3's
    time scale picks it: the nearest rung of the ladder by ratio, or whole
    years in steps of 1, 2 or 5 x 10^k beyond a year."""
    if isinstance(count, bool) or not isinstance(count, int) or count < 1:
        raise ValueError(f"count must be a whole number of at least 1, got {count}")
    span = abs(epoch_day_from_iso(stop) - epoch_day_from_iso(start))
    target = span / count
    i = 0
    while i < len(_LADDER) and _LADDER[i][2] <= target:
        i += 1
    if i == len(_LADDER):
        return TimeTickInterval(interval="year", step=max(1, int(tick_step(0, span / 365, count))))
    if i == 0:
        return TimeTickInterval(interval="day", step=1)
    pick = _LADDER[i - 1] if target / _LADDER[i - 1][2] < _LADDER[i][2] / target else _LADDER[i]
    return TimeTickInterval(interval=pick[0], step=pick[1])