from .charts_ticks_tick_step import tick_step from .charts_ticks_types import TimeTickInterval from .dates_add_days import epoch_day_from_iso # 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])