Delete date helpers nothing calls

A hand-rolled month-arithmetic module survived the move to dateutil with no
caller left but its own tests, and two predicates alongside it were never
wired up at all. An error import was also being deferred inside a function
for a circular dependency that does not exist.
This commit is contained in:
2026-09-18 01:09:42 +02:00
parent 4fa93c4985
commit 6a79435baf
6 changed files with 15 additions and 320 deletions
-64
View File
@@ -1,64 +0,0 @@
"""Date calculation utilities for leave planning.
Pure functions with no I/O or state. Used by planning and analytics services.
"""
import datetime
def add_months(date: datetime.date, n: int) -> datetime.date:
"""Add or subtract months from a date, handling month-end edge cases.
Examples:
add_months(datetime.date(2026, 1, 31), 1) -> 2026-02-28 (or 29 in leap year)
add_months(datetime.date(2026, 3, 31), -1) -> 2026-02-28
"""
month = date.month + n
year = date.year
while month > 12:
month -= 12
year += 1
while month < 1:
month += 12
year -= 1
# Handle month-end: if original day doesn't exist in target month,
# use last day of that month
day = min(date.day, _days_in_month(year, month))
return datetime.date(year, month, day)
def _days_in_month(year: int, month: int) -> int:
"""Return number of days in the given month."""
if month == 2:
return 29 if _is_leap_year(year) else 28
if month in (4, 6, 9, 11):
return 30
return 31
def _is_leap_year(year: int) -> bool:
"""Check if a year is a leap year."""
return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
def age_in_months(birth_date: datetime.date, on: datetime.date) -> int:
"""Calculate exact age in months between two dates.
If on < birth_date, returns negative value (person not yet born).
"""
months = (on.year - birth_date.year) * 12 + (on.month - birth_date.month)
# Adjust if birthday hasn't occurred this month yet
if on.day < birth_date.day:
months -= 1
return months
def fourth_birthday(birth_date: datetime.date) -> datetime.date:
"""Return the date when a child turns 4 years old."""
return add_months(birth_date, 48)
-6
View File
@@ -183,12 +183,6 @@ def is_swedish_holiday(date: datetime.date) -> bool:
return date in holidays_for_year
def get_holiday_name(date: datetime.date) -> str | None:
"""Get the name of a Swedish public holiday, or None if not a holiday."""
holidays_for_year = get_swedish_holidays(date.year)
return holidays_for_year.get(date)
def is_non_working_day(date: datetime.date) -> bool:
"""True if `date` is a weekend day, a public holiday, or a de facto day
off (julafton, midsommarafton, nyårsafton, ...).
+14 -6
View File
@@ -13,7 +13,7 @@ from datetime import date, timedelta
from dateutil.relativedelta import relativedelta
from app.config import settings
from app.config import LeaveRules, settings
from app.entities.child import Child
from app.entities.personal_data import PersonalData
from app.entities.planned_leave import PlannedLeaveDay
@@ -32,6 +32,19 @@ def weekly_intensity(planned_days: list[PlannedLeaveDay], period_weeks: float) -
return len(fp_days) / period_weeks
def default_tier_pool_share(rules: LeaveRules) -> tuple[int, int]:
"""The per-parent (sjukpenning_left, lagsta_left) assumed when no FK
snapshot exists yet.
`rules.sjukpenning_days`/`lagsta_niva_days` are the WHOLE CHILD's combined
tier totals (390 + 90), split across both parents — halving them gives a
single parent's default share. Shared by `AllocationInputLoader.load` and
`PlanningService._tier_pool_left` so they can never derive it differently
(see "Bug A": they once did, 390/90 in one place vs. 195/45 in the other).
"""
return rules.sjukpenning_days // 2, rules.lagsta_niva_days // 2
def resolve_age4_savable_cap(planning_mode: str, parent_count: int) -> int:
"""Single source of truth for which age-4 savable-days ceiling applies.
@@ -100,11 +113,6 @@ def age4_pooling_loss(
return max(reachable_pooled - reachable_per_parent, 0)
def is_child_under_age(birth_date: date, months: int) -> bool:
"""True if the child's age in months is less than `months`."""
return date.today() < birth_date + relativedelta(months=months)
def is_child_over_age(birth_date: date, years: int) -> bool:
"""True if the child has turned `years`."""
return date.today() >= birth_date + relativedelta(years=years)