Initial commit
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
"""Date calculation utilities for leave planning.
|
||||
|
||||
Pure functions with no I/O or state. Used by planning and analytics services.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
from typing import Union
|
||||
|
||||
|
||||
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)
|
||||
Reference in New Issue
Block a user